From 2ba875a4749320d61c5376b937b529a12d33f21b Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Wed, 30 Oct 2019 11:26:18 -0700 Subject: [PATCH] Word wrapping works! --- 4ed_api_implementation.cpp | 5 - custom/4coder_default_hooks.cpp | 185 +------------- custom/4coder_default_include.cpp | 1 + custom/4coder_layout_rule.cpp | 390 ++++++++++++++++++++++++++++++ 4 files changed, 393 insertions(+), 188 deletions(-) create mode 100644 custom/4coder_layout_rule.cpp diff --git a/4ed_api_implementation.cpp b/4ed_api_implementation.cpp index 4bf61740..c9b7802d 100644 --- a/4ed_api_implementation.cpp +++ b/4ed_api_implementation.cpp @@ -2717,8 +2717,6 @@ draw_rectangle(Application_Links *app, Rect_f32 rect, f32 roundness, FColor colo if (models->in_render_mode){ Color_Table color_table = models->color_table; u32 actual_color = finalize_color(color_table, color); - f32 scale = system_get_screen_scale_factor(); - roundness *= scale; draw_rectangle(models->target, rect, roundness, actual_color); } } @@ -2730,9 +2728,6 @@ draw_rectangle_outline(Application_Links *app, Rect_f32 rect, if (models->in_render_mode){ Color_Table color_table = models->color_table; u32 actual_color = finalize_color(color_table, color); - f32 scale = system_get_screen_scale_factor(); - roundness *= scale; - thickness *= scale; draw_rectangle_outline(models->target, rect, roundness, thickness, actual_color); } } diff --git a/custom/4coder_default_hooks.cpp b/custom/4coder_default_hooks.cpp index f5ade07d..a2628cf4 100644 --- a/custom/4coder_default_hooks.cpp +++ b/custom/4coder_default_hooks.cpp @@ -281,188 +281,6 @@ default_buffer_region(Application_Links *app, View_ID view_id, Rect_f32 region){ return(region); } -internal void -buffer_layout__write(Arena *arena, Layout_Item_List *list, - i64 index, u32 codepoint, Layout_Item_Flag flags, Rect_f32 rect){ - Temp_Memory restore_point = begin_temp(arena); - Layout_Item *item = push_array(arena, Layout_Item, 1); - - Layout_Item_Block *block = list->first; - if (block != 0){ - if (block->items + block->count == item){ - block->count += 1; - } - else{ - block = 0; - } - } - if (block == 0){ - end_temp(restore_point); - block = push_array(arena, Layout_Item_Block, 1); - item = push_array(arena, Layout_Item, 1); - sll_queue_push(list->first, list->last, block); - list->node_count += 1; - block->items = item; - block->count = 1; - } - list->total_count += 1; - - if (index > list->index_range.max){ - block->character_count += 1; - list->character_count += 1; - list->index_range.max = index; - } - - item->index = index; - item->codepoint = codepoint; - item->flags = flags; - item->rect = rect; - list->height = max(list->height, rect.y1); -} - -function Layout_Item_List -default_buffer_layout(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, - Face_ID face, f32 width){ - Scratch_Block scratch(app); - - Layout_Item_List list = {}; - list.index_range.first = range.first; - list.index_range.one_past_last = range.first - 1; - - String_Const_u8 text = push_buffer_range(app, scratch, buffer, range); - - Face_Metrics metrics = get_face_metrics(app, face); - f32 line_height = metrics.line_height; - f32 text_height = metrics.text_height; - f32 line_to_text_shift = text_height - line_height; - f32 space_advance = metrics.space_advance; - - Face_Advance_Map advance_map = get_face_advance_map(app, face); - - if (text.size == 0){ - f32 next_x = space_advance; - buffer_layout__write(arena, &list, range.first, ' ', 0, - Rf32(V2(0.f, 0.f), V2f32(next_x, text_height))); - } - else{ - Vec2_f32 p = {}; - f32 line_y = line_height; - f32 text_y = text_height; - - i64 index = range.first; - b32 first_of_the_line = true; - - b32 consuming_newline_characters = false; - i64 newline_character_index = -1; - - b32 prev_did_emit_newline = false; - - u8 *ptr = text.str; - u8 *end_ptr = ptr + text.size; - for (;ptr < end_ptr;){ - Character_Consume_Result consume = utf8_consume(ptr, (umem)(end_ptr - ptr)); - u32 render_codepoint = consume.codepoint; - b32 emit_newline = false; - switch (consume.codepoint){ - case '\t': - { - render_codepoint = ' '; - }//fallthrough; - default: - { - f32 advance = font_get_glyph_advance(&advance_map, &metrics, - consume.codepoint); - f32 next_x = p.x + advance; - if (!first_of_the_line && next_x >= width){ - p.y = line_y; - p.x = 0.f; - line_y += line_height; - text_y = line_y + line_to_text_shift; - next_x = p.x + advance; - } - buffer_layout__write(arena, &list, index, render_codepoint, 0, - Rf32(p, V2f32(next_x, text_y))); - p.x = next_x; - ptr += consume.inc; - index += consume.inc; - first_of_the_line = false; - }break; - - case '\r': - { - if (!consuming_newline_characters){ - consuming_newline_characters = true; - newline_character_index = index; - } - ptr += 1; - index += 1; - }break; - - case '\n': - { - if (!consuming_newline_characters){ - consuming_newline_characters = true; - newline_character_index = index; - } - emit_newline = true; - ptr += 1; - index += 1; - }break; - - case max_u32: - { - f32 next_x = p.x + metrics.byte_advance; - if (!first_of_the_line && next_x >= width){ - p.y = line_y; - p.x = 0.f; - line_y += line_height; - text_y = line_y + line_to_text_shift; - next_x = p.x + metrics.byte_advance; - } - u32 v = *ptr; - u32 lo = v&0xF; - u32 hi = (v >> 4)&0xF; - f32 advance = metrics.byte_sub_advances[0]; - buffer_layout__write(arena, &list, index, '\\', 0, - Rf32(p, V2f32(p.x + advance, text_y))); - p.x += advance; - advance = metrics.byte_sub_advances[1]; - buffer_layout__write(arena, &list, index, integer_symbols[lo], 0, - Rf32(p, V2f32(p.x + advance, text_y))); - p.x += advance; - advance = metrics.byte_sub_advances[2]; - buffer_layout__write(arena, &list, index, integer_symbols[hi], 0, - Rf32(p, V2f32(p.x + advance, text_y))); - p.x = next_x; - ptr += 1; - index += 1; - first_of_the_line = false; - }break; - } - prev_did_emit_newline = false; - if (emit_newline){ - f32 next_x = p.x + space_advance; - buffer_layout__write(arena, &list, newline_character_index, ' ', 0, - Rf32(p, V2f32(next_x, text_y))); - p.y = line_y; - p.x = 0.f; - line_y += line_height; - text_y = line_y + line_to_text_shift; - first_of_the_line = true; - prev_did_emit_newline = true; - } - } - if (!prev_did_emit_newline){ - f32 next_x = p.x + space_advance; - buffer_layout__write(arena, &list, index, ' ', 0, Rf32(p, V2f32(next_x, text_y))); - } - } - list.bottom_extension = -line_to_text_shift; - list.height += list.bottom_extension; - - return(list); -} - function void default_render_buffer(Application_Links *app, View_ID view_id, b32 is_active_view, Buffer_ID buffer, Text_Layout_ID text_layout_id, @@ -1113,7 +931,8 @@ set_all_default_hooks(Application_Links *app){ set_custom_hook(app, HookID_SaveFile, default_file_save); set_custom_hook(app, HookID_BufferEditRange, default_buffer_edit_range); set_custom_hook(app, HookID_BufferRegion, default_buffer_region); - set_custom_hook(app, HookID_Layout, default_buffer_layout); + + set_custom_hook(app, HookID_Layout, layout_wrap_whitespace); } // BOTTOM diff --git a/custom/4coder_default_include.cpp b/custom/4coder_default_include.cpp index f81f8884..bd21e883 100644 --- a/custom/4coder_default_include.cpp +++ b/custom/4coder_default_include.cpp @@ -80,6 +80,7 @@ #include "4coder_font_helper.cpp" #include "4coder_config.cpp" #include "4coder_default_framework.cpp" +#include "4coder_layout_rule.cpp" #include "4coder_lister_base.cpp" #include "4coder_base_commands.cpp" #include "4coder_insertion.cpp" diff --git a/custom/4coder_layout_rule.cpp b/custom/4coder_layout_rule.cpp new file mode 100644 index 00000000..b60090e6 --- /dev/null +++ b/custom/4coder_layout_rule.cpp @@ -0,0 +1,390 @@ +/* +4coder_layout_rule.cpp - Built in layout rules and layout rule helpers. +*/ + +// TOP + +function void +layout_write(Arena *arena, Layout_Item_List *list, + i64 index, u32 codepoint, Layout_Item_Flag flags, Rect_f32 rect){ + Temp_Memory restore_point = begin_temp(arena); + Layout_Item *item = push_array(arena, Layout_Item, 1); + + Layout_Item_Block *block = list->first; + if (block != 0){ + if (block->items + block->count == item){ + block->count += 1; + } + else{ + block = 0; + } + } + if (block == 0){ + end_temp(restore_point); + block = push_array(arena, Layout_Item_Block, 1); + item = push_array(arena, Layout_Item, 1); + sll_queue_push(list->first, list->last, block); + list->node_count += 1; + block->items = item; + block->count = 1; + } + list->total_count += 1; + + if (index > list->index_range.max){ + block->character_count += 1; + list->character_count += 1; + list->index_range.max = index; + } + + item->index = index; + item->codepoint = codepoint; + item->flags = flags; + item->rect = rect; + list->height = max(list->height, rect.y1); +} + +function Layout_Item_List +layout_wrap_anywhere(Application_Links *app, Arena *arena, Buffer_ID buffer, + Range_i64 range, Face_ID face, f32 width){ + Scratch_Block scratch(app); + + Layout_Item_List list = {}; + list.index_range.first = range.first; + list.index_range.one_past_last = range.first - 1; + + String_Const_u8 text = push_buffer_range(app, scratch, buffer, range); + + Face_Advance_Map advance_map = get_face_advance_map(app, face); + Face_Metrics metrics = get_face_metrics(app, face); + f32 line_height = metrics.line_height; + f32 text_height = metrics.text_height; + f32 line_to_text_shift = text_height - line_height; + f32 space_advance = metrics.space_advance; + + if (text.size == 0){ + f32 next_x = space_advance; + layout_write(arena, &list, range.first, ' ', 0, + Rf32(V2(0.f, 0.f), V2f32(next_x, text_height))); + } + else{ + Vec2_f32 p = {}; + f32 line_y = line_height; + f32 text_y = text_height; + + i64 index = range.first; + + b32 first_of_the_line = true; + b32 consuming_newline_characters = false; + i64 newline_character_index = -1; + b32 prev_did_emit_newline = false; + + u8 *ptr = text.str; + u8 *end_ptr = ptr + text.size; + for (;ptr < end_ptr;){ + Character_Consume_Result consume = utf8_consume(ptr, (umem)(end_ptr - ptr)); + u32 render_codepoint = consume.codepoint; + b32 emit_newline = false; + switch (consume.codepoint){ + case '\t': + { + render_codepoint = ' '; + }//fallthrough; + default: + { + f32 advance = font_get_glyph_advance(&advance_map, &metrics, + consume.codepoint); + f32 next_x = p.x + advance; + if (!first_of_the_line && next_x > width){ + p.y = line_y; + p.x = 0.f; + line_y += line_height; + text_y = line_y + line_to_text_shift; + next_x = advance; + } + layout_write(arena, &list, index, + render_codepoint, 0, + Rf32(p, V2f32(next_x, text_y))); + p.x = next_x; + ptr += consume.inc; + index += consume.inc; + first_of_the_line = false; + }break; + + case '\r': + { + if (!consuming_newline_characters){ + consuming_newline_characters = true; + newline_character_index = index; + } + ptr += 1; + index += 1; + }break; + + case '\n': + { + if (!consuming_newline_characters){ + consuming_newline_characters = true; + newline_character_index = index; + } + emit_newline = true; + ptr += 1; + index += 1; + }break; + + case max_u32: + { + f32 next_x = p.x + metrics.byte_advance; + if (!first_of_the_line && next_x > width){ + p.y = line_y; + p.x = 0.f; + line_y += line_height; + text_y = line_y + line_to_text_shift; + next_x = p.x + metrics.byte_advance; + } + u32 v = *ptr; + u32 lo = v&0xF; + u32 hi = (v >> 4)&0xF; + f32 advance = metrics.byte_sub_advances[0]; + layout_write(arena, &list, index, '\\', 0, + Rf32(p, V2f32(p.x + advance, text_y))); + p.x += advance; + advance = metrics.byte_sub_advances[1]; + layout_write(arena, &list, index, integer_symbols[hi], 0, + Rf32(p, V2f32(p.x + advance, text_y))); + p.x += advance; + advance = metrics.byte_sub_advances[2]; + layout_write(arena, &list, index, integer_symbols[lo], 0, + Rf32(p, V2f32(p.x + advance, text_y))); + p.x = next_x; + ptr += 1; + index += 1; + first_of_the_line = false; + }break; + } + + prev_did_emit_newline = false; + if (emit_newline){ + f32 next_x = p.x + space_advance; + layout_write(arena, &list, newline_character_index, ' ', 0, + Rf32(p, V2f32(next_x, text_y))); + p.y = line_y; + p.x = 0.f; + line_y += line_height; + text_y = line_y + line_to_text_shift; + first_of_the_line = true; + prev_did_emit_newline = true; + } + } + + if (!prev_did_emit_newline){ + f32 next_x = p.x + space_advance; + layout_write(arena, &list, index, ' ', 0, + Rf32(p, V2f32(next_x, text_y))); + } + } + list.bottom_extension = -line_to_text_shift; + list.height += list.bottom_extension; + + return(list); +} + +function Layout_Item_List +layout_wrap_whitespace(Application_Links *app, Arena *arena, Buffer_ID buffer, + Range_i64 range, Face_ID face, f32 width){ + Scratch_Block scratch(app); + + Layout_Item_List list = {}; + list.index_range.first = range.first; + list.index_range.one_past_last = range.first - 1; + + String_Const_u8 text = push_buffer_range(app, scratch, buffer, range); + + Face_Advance_Map advance_map = get_face_advance_map(app, face); + Face_Metrics metrics = get_face_metrics(app, face); + f32 line_height = metrics.line_height; + f32 text_height = metrics.text_height; + f32 line_to_text_shift = text_height - line_height; + f32 space_advance = metrics.space_advance; + + if (text.size == 0){ + f32 next_x = space_advance; + layout_write(arena, &list, range.first, ' ', 0, + Rf32(V2(0.f, 0.f), V2f32(next_x, text_height))); + } + else{ + Vec2_f32 p = {}; + f32 line_y = line_height; + f32 text_y = text_height; + + b32 first_of_the_line = true; + b32 consuming_newline_characters = false; + i64 newline_character_index = -1; + b32 prev_did_emit_newline = false; + + u8 *ptr = text.str; + u8 *end_ptr = ptr + text.size; + u8 *word_ptr = ptr; + + if (character_is_whitespace(*ptr)){ + goto consuming_whitespace; + } + + consuming_non_whitespace: + consuming_newline_characters = false; + newline_character_index = -1; + + for (;ptr <= end_ptr; ptr += 1){ + if (ptr == end_ptr || character_is_whitespace(*ptr)){ + break; + } + } + + { + String_Const_u8 word = SCu8(word_ptr, ptr); + u8 *word_end = ptr; + + if (!first_of_the_line){ + f32 total_advance = 0.f; + ptr = word.str; + for (; ptr < word_end;){ + Character_Consume_Result consume = + utf8_consume(ptr, (umem)(word_end - ptr)); + if (consume.codepoint != max_u32){ + f32 advance = font_get_glyph_advance(&advance_map, &metrics, + consume.codepoint); + total_advance += advance; + } + else{ + total_advance += metrics.byte_advance; + } + ptr += consume.inc; + } + + f32 next_x = p.x + total_advance; + if (next_x > width){ + p.y = line_y; + p.x = 0.f; + line_y += line_height; + text_y = line_y + line_to_text_shift; + next_x = total_advance; + } + } + + ptr = word.str; + + for (; ptr < word_end;){ + Character_Consume_Result consume = + utf8_consume(ptr, (umem)(word_end - ptr)); + + if (consume.codepoint != max_u32){ + f32 advance = font_get_glyph_advance(&advance_map, &metrics, + consume.codepoint); + i64 index = (i64)(ptr - text.str) + range.first; + layout_write(arena, &list, index, + consume.codepoint, 0, + Rf32(p, V2f32(p.x + advance, text_y))); + p.x += advance; + } + else{ + u32 v = *ptr; + u32 lo = v&0xF; + u32 hi = (v >> 4)&0xF; + i64 index = (i64)(ptr - text.str) + range.first; + f32 advance = metrics.byte_sub_advances[0]; + layout_write(arena, &list, index, '\\', 0, + Rf32(p, V2f32(p.x + advance, text_y))); + p.x += advance; + advance = metrics.byte_sub_advances[1]; + layout_write(arena, &list, index, integer_symbols[hi], 0, + Rf32(p, V2f32(p.x + advance, text_y))); + p.x += advance; + advance = metrics.byte_sub_advances[2]; + layout_write(arena, &list, index, integer_symbols[lo], 0, + Rf32(p, V2f32(p.x + advance, text_y))); + p.x += advance; + } + + ptr += consume.inc; + } + + first_of_the_line = false; + } + + consuming_whitespace: + for (; ptr < end_ptr; ptr += 1){ + if (!character_is_whitespace(*ptr)){ + word_ptr = ptr; + goto consuming_non_whitespace; + } + + b32 emit_newline = false; + + switch (*ptr){ + default: + { + f32 advance = space_advance; + if (*ptr == '\t'){ + advance *= 4.f; + } + f32 next_x = p.x + advance; + if (!first_of_the_line && next_x > width){ + p.y = line_y; + p.x = 0.f; + line_y += line_height; + text_y = line_y + line_to_text_shift; + next_x = advance; + } + i64 index = (i64)(ptr - text.str) + range.first; + layout_write(arena, &list, index, + ' ', 0, + Rf32(p, V2f32(next_x, text_y))); + p.x = next_x; + first_of_the_line = false; + }break; + + case '\r': + { + if (!consuming_newline_characters){ + consuming_newline_characters = true; + newline_character_index = (i64)(ptr - text.str) + range.first; + } + }break; + + case '\n': + { + if (!consuming_newline_characters){ + consuming_newline_characters = true; + newline_character_index = (i64)(ptr - text.str) + range.first; + } + emit_newline = true; + }break; + } + + if (emit_newline){ + f32 next_x = p.x + space_advance; + layout_write(arena, &list, newline_character_index, ' ', 0, + Rf32(p, V2f32(next_x, text_y))); + p.y = line_y; + p.x = 0.f; + line_y += line_height; + text_y = line_y + line_to_text_shift; + first_of_the_line = true; + } + prev_did_emit_newline = emit_newline; + } + + if (!prev_did_emit_newline){ + f32 next_x = p.x + space_advance; + i64 index = (i64)(ptr - text.str) + range.first; + layout_write(arena, &list, index, ' ', 0, + Rf32(p, V2f32(next_x, text_y))); + } + } + list.bottom_extension = -line_to_text_shift; + list.height += list.bottom_extension; + + return(list); +} + +// BOTTOM +