void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; // TODO(allen): Use giant source files! internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } // TODO(allen): Use really big code files! for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; // deepinfile internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; } /* * Mr. 4th Dimention - Allen Webster * * 12.12.2014 * * Application layer for project codename "4ed" * */ struct Partition_Data{ u8 *memory_base; u8 *memory_cursor; u32 max_size; }; internal void* partition_allocate(Partition_Data *data, u32 size){ void *ret = 0; if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ ret = data->memory_cursor; data->memory_cursor += size; } return ret; } struct Cursor_Data{ u32 pos; u32 line_number, line_off; }; struct Panel_Cursor_Data{ u32 pos; u32 x, y; }; struct Editing_Data{ u32 size, max_size; void *data; Cursor_Data cursor; }; struct Editing_Style{ Glyph_Set *set; u32 cursor_color; u32 default_color; u32 at_cursor_color; }; struct Editing_Panel{ i32 x, y; i32 w, h; u32 tab_width; Panel_Cursor_Data cursor; u32 scroll_y; Editing_Data *buffer; Editing_Style *style; }; /* * Buffer Functions */ // TODO(allen): // // function(s) for computing cursor_pos from line/off for skip to position // function(s) for computing line/off from cursor_pos // // handle \r \n and \r\n modes better search for RNRN // // non-line wrapping rendering function // // handle buffer out-of-memory situations // // buffer open / save // internal bool32 buffer_open(Editing_Data *buffer, c_str filename, void *data_space, u32 data_space_size){ bool32 result = 0; File_Data file = system_load_file(filename); if (file.size < data_space_size){ result = 1; buffer->size = file.size; buffer->data = data_space; buffer->max_size = data_space_size; for (u32 i = 0; i < file.size; ++i){ ((u8*)buffer->data)[i] = ((u8*)file.data)[i]; } ((u8*)buffer->data)[buffer->size] = 0; } system_free_file(file); return result; } internal Panel_Cursor_Data panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (cursor_pos < pos || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal Panel_Cursor_Data panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ Panel_Cursor_Data result = {}; u32 cursor_pos = 0; u32 counted_x = 0; u32 counted_y = 0; u8 *data = (u8*)panel->buffer->data; u32 size = panel->buffer->size; u32 character_w = panel->style->set->glyphs[' '].advance; u32 max_line_length = panel->w / character_w; // NOTE(allen): RNRN, this is made for rn mode while (cursor_pos < size && (counted_y < cursor_y || counted_x < cursor_x || data[cursor_pos] == '\r') ){ if (data[cursor_pos] == '\r'){ // DO NOTHING } else if (data[cursor_pos] == '\n' || counted_x+1 >= max_line_length){ ++counted_y; if (counted_y > cursor_y){ // TODO(allen): error here? // return error code? pointer out? struct? // just give closest possible? silent in editing_data? --counted_y; break; } counted_x = 0; } else if (data[cursor_pos] == '\t'){ counted_x += panel->tab_width; } else{ ++counted_x; } ++cursor_pos; } result.pos = cursor_pos; result.x = counted_x; result.y = counted_y; return result; } internal void panel_cursor_match_to_panel(Editing_Panel *panel){ panel->buffer->cursor.pos = panel->cursor.pos; } internal bool32 buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ bool32 result = 0; if (editing_data->size + 1 < editing_data->max_size){ result = 1; u8 *data = (u8*)editing_data->data; for (i32 pos = editing_data->size; pos >= (i32)editing_data->cursor.pos; --pos){ data[pos+1] = data[pos]; } data[editing_data->cursor.pos] = character; ++editing_data->size; } else{ // TODO(allen): automatically try to fix right here when this // happens, or try to avoid this at all costs? } return result; } internal void panel_insert(Editing_Panel *panel, u8 character){ Editing_Data *editing_data = panel->buffer; if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ ++editing_data->cursor.pos; panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } } internal void panel_move_left(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u8 *data = (u8*)editing_data->data; if (editing_data->cursor.pos > 0){ --editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos > 0){ --editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal void panel_move_right(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; u32 size = editing_data->size; u8* data = (u8*)editing_data->data; if (editing_data->cursor.pos < size){ ++editing_data->cursor.pos; // NOTE(allen): RNRN, this is made for rn mode while (data[editing_data->cursor.pos] == '\r' && editing_data->cursor.pos < size){ ++editing_data->cursor.pos; } } panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); panel_cursor_match_to_panel(panel); } internal bool32 buffer_delete(Editing_Data *editing_data, u32 pos){ bool32 did_delete = 0; if (pos >= 0 && pos < editing_data->size){ did_delete = 1; i32 size = (i32)(--editing_data->size); u8 *data = (u8*)editing_data->data; for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ data[adjust_pos] = data[adjust_pos+1]; } } return did_delete; } internal void panel_backspace(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->cursor.pos > 0 && buffer_delete(editing_data, editing_data->cursor.pos-1)){ panel->cursor = panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); panel_cursor_match_to_panel(panel); } } internal void panel_delete(Editing_Panel *panel){ Editing_Data *editing_data = panel->buffer; if (editing_data->size > 0){ buffer_delete(editing_data, editing_data->cursor.pos); } } internal void panel_move_up(Editing_Panel *panel){ if (panel->cursor.y > 0){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y-1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } } internal void panel_move_down(Editing_Panel *panel){ Panel_Cursor_Data newpos = panel_compute_cursor_from_xy(panel, panel->cursor.x, panel->cursor.y+1); panel->cursor = newpos; panel_cursor_match_to_panel(panel); } internal void panel_basic_mode_key_event(Key_Codes *codes, Editing_Panel *panel, Key_Event_Data key_data){ u16 character = key_data.character; if (character != 0){ panel_insert(panel, (u8)character); }else{ if (key_data.keycode == codes->left){ panel_move_left(panel); } else if (key_data.keycode == codes->right){ panel_move_right(panel); } else if (key_data.keycode == codes->back){ panel_backspace(panel); } else if (key_data.keycode == codes->del){ panel_delete(panel); } else if (key_data.keycode == codes->up){ panel_move_up(panel); } else if (key_data.keycode == codes->down){ panel_move_down(panel); } } } internal void panel_draw(Render_Target *target, Editing_Panel *panel, bool32 is_active){ Editing_Style *style = panel->style; Glyph_Set *set = style->set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 offset_x = panel->x; i32 offset_y = panel->y; i32 max_x = panel->w; i32 max_y = panel->h; i32 max_line_length = max_x / character_w; i32 max_lines = max_y / character_h; AllowLocal(max_line_length); AllowLocal(max_lines); u32 tab_width = panel->tab_width; u32 size = panel->buffer->size; u8 *data = (u8*)panel->buffer->data; Panel_Cursor_Data start_cursor; start_cursor = panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); u32 start_character = start_cursor.pos; i32 pos_x = 0; i32 pos_y = 0; for (u32 character_i = start_character; character_i < size && data[character_i]; ++character_i){ if (pos_x + character_w > max_x){ pos_x = 0; pos_y += set->line_skip; } u8 to_render = data[character_i]; if (character_i == panel->cursor.pos){ if (is_active){ draw_rectangle(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } else{ draw_rectangle_outline(target, offset_x + pos_x, offset_y + pos_y, character_w, set->line_skip, style->cursor_color); } } // NOTE(allen): RNRN, made for rn mode if (to_render == '\r'){ // DO NOTHING } else if (to_render == '\n'){ pos_x = 0; pos_y += set->line_skip; } else if (to_render == '\t'){ pos_x += character_w*tab_width; } else if (set->glyphs[to_render].data){ i32 advance = set->glyphs[to_render].advance; u32 char_color = style->default_color; if (character_i == panel->cursor.pos && is_active){ char_color = style->at_cursor_color; } font_draw_glyph(target, set, to_render, offset_x + pos_x, offset_y + pos_y, char_color); pos_x += advance; } else{ i32 advance = character_w; pos_x += advance; } if (pos_y + set->line_skip > max_y){ break; } } } struct App_Vars{ Glyph_Set glyph_set; Editing_Data buffer, buffer2; i32 active_panel; i32 prev_width, prev_height; Editing_Panel panels[2]; Editing_Style style; i32 last_click_x, last_click_y; }; internal bool32 app_init(Application_Memory *memory){ if (font_init() != 0){ FatalError("Error initializing fonts"); return 0; } Partition_Data partition = {}; partition.memory_base = partition.memory_cursor = (u8*)memory->main_memory; partition.max_size = memory->main_memory_size; memory->font_memory_size = Kbytes(512); memory->font_memory = partition_allocate(&partition, memory->font_memory_size); Assert(memory->font_memory); memory->vars_memory_size = sizeof(App_Vars); memory->vars_memory = partition_allocate(&partition, memory->vars_memory_size); Assert(memory->vars_memory); memory->buffer_memory_size = memory->main_memory_size - (memory->vars_memory_size + memory->font_memory_size); memory->buffer_memory = partition_allocate(&partition, memory->buffer_memory_size); Assert(memory->buffer_memory); App_Vars *vars = (App_Vars*)memory->vars_memory; // NOTE(allen): font setup i32 memory_used = 0; if (font_load(&vars->glyph_set, 15, memory->font_memory, font_predict_size(15), &memory_used) != 0){ FatalError("Could not find any fonts"); } // NOTE(allen): buffer setup u32 buffer_width = Mbytes(1); u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; vars->buffer = {}; buffer_open(&vars->buffer, "../code/win32_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; vars->buffer2 = {}; buffer_open(&vars->buffer2, "long_4ed.cpp", buffer_memory_cursor, buffer_width); buffer_memory_cursor += buffer_width; // NOTE(allen): style and panels setup vars->style.set = &vars->glyph_set; vars->style.cursor_color = 0xFF00FF00; vars->style.default_color = 0xFFFFFFFF; vars->style.at_cursor_color = 0xFF222222; Editing_Panel *panels = vars->panels; panels[0].tab_width = 4; panels[0].cursor = {}; panels[0].scroll_y = 0; panels[0].buffer = &vars->buffer; panels[0].style = &vars->style; panels[1].tab_width = 4; panels[1].cursor = {}; panels[1].scroll_y = 0; panels[1].buffer = &vars->buffer2; panels[1].style = &vars->style; return 1; } internal u8* str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ u8 *result = 0; bool32 is_negative = (number < 0); if (is_negative){ number = -number; } if (number == 0){ buffer_space[buffer_size - 1] = 0; buffer_space[buffer_size - 2] = '0'; result = buffer_space + buffer_size - 2; } else{ buffer_space[buffer_size - 1] = 0; u32 str_write_pos = buffer_size - 1; while (number > 0 && str_write_pos > 0){ --str_write_pos; u8 to_write = (u8)(number % 10); number /= 10; buffer_space[str_write_pos] = '0' + to_write; } if (is_negative && str_write_pos > 0){ --str_write_pos; buffer_space[str_write_pos] = '-'; } result = buffer_space + str_write_pos; } return result; } internal void app_step(Key_Codes *codes, Key_Input_Data *input, Mouse_State *mouse, bool32 time_step, Render_Target *target, Application_Memory *memory){ App_Vars *vars = (App_Vars*)memory->vars_memory; u32 background_color = 0xFF0C0C0C; i32 debug_bar_height = vars->glyph_set.line_skip; Editing_Panel *panels = vars->panels; panels[0].x = 2; panels[0].y = debug_bar_height; panels[0].w = target->width / 2 - 4; panels[0].h = target->height - debug_bar_height; panels[1].x = panels[0].x + panels[0].w + 5; panels[1].y = debug_bar_height; panels[1].w = target->width / 2 - 5; panels[1].h = target->height - debug_bar_height; draw_clear(target, background_color); if (vars->prev_width != target->width || vars->prev_height != target->height){ for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ panels[panel_i].cursor = panel_compute_cursor_from_pos(&panels[panel_i], panels[panel_i].cursor.pos); } } if (time_step){ i32 mx = mouse->x; i32 my = mouse->y; i32 clicked_panel = -1; bool32 mouse_press_event = 0; if (mouse->left_button && !mouse->left_button_prev){ mouse_press_event = 1; } for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ Glyph_Set *set = &vars->glyph_set; i32 character_w = set->glyphs[' '].advance; i32 character_h = set->line_skip; i32 max_line_length = panels[panel_i].w / character_w; i32 max_lines = panels[panel_i].h / character_h; if (mouse_press_event && // NOTE(allen): check if mouse event has occured mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel mx < panels[panel_i].w + panels[panel_i].x && my >= panels[panel_i].y && my < panels[panel_i].h + panels[panel_i].y){ if (clicked_panel == -1){ clicked_panel = panel_i; i32 grid_x = (mx - panels[panel_i].x) / character_w; i32 grid_y = (my - panels[panel_i].y) / character_h; vars->last_click_x = grid_x; vars->last_click_y = grid_y; vars->active_panel = panel_i; Editing_Panel *active_panel = &panels[panel_i]; if (grid_x >= 0 && grid_x < max_line_length && grid_y >= 0 && grid_y < max_lines){ i32 pos_x = grid_x; i32 pos_y = grid_y + active_panel->scroll_y; active_panel->cursor = panel_compute_cursor_from_xy(active_panel, pos_x, pos_y); panel_cursor_match_to_panel(active_panel); } } else{ // TODO(allen): debug diagnostics, overlapped panels? } } } Editing_Panel *active_panel = &panels[vars->active_panel]; if (!input->control_keys[CONTROL_KEY_CONTROL] && !input->control_keys[CONTROL_KEY_ALT]){ for (u32 i = 0; i < input->press_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->presses[i]); } for (u32 i = 0; i < input->hold_count; ++i){ panel_basic_mode_key_event(codes, active_panel, input->holds[i]); } } // TODO(allen): abstract the max_lines/max_line_length computations u32 cursor_y = active_panel->cursor.y; u32 scroll_y = active_panel->scroll_y; u32 character_h = active_panel->style->set->line_skip; u32 max_lines = active_panel->h / character_h; while (cursor_y >= scroll_y + max_lines){ scroll_y += Max(1, max_lines / 4); } while (cursor_y < scroll_y){ scroll_y -= Max(1, max_lines / 4); } active_panel->scroll_y = scroll_y; } panel_draw(target, &panels[0], vars->active_panel == 0); draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); panel_draw(target, &panels[1], vars->active_panel == 1); // NOTE(allen): debug bar Glyph_Set *set = &vars->glyph_set; draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); u8 spacer_str[] = " -- "; u32 char_color = 0xFF00FFFF; i32 pos_x = 0; i32 pos_y = 0; Editing_Panel *active_panel = &panels[vars->active_panel]; u8 str[16]; u8 *str_out; str_out= str_from_int(vars->active_panel, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)active_panel->cursor.y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_x, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } for (i32 i = 0; spacer_str[i]; ++i){ font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); pos_x += set->glyphs[spacer_str[i]].advance; } str_out = str_from_int((i32)vars->last_click_y, str, 16); for (i32 i = 0; str_out[i]; ++i){ font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); pos_x += set->glyphs[str_out[i]].advance; } vars->prev_width = target->width; vars->prev_height = target->height; }