diff --git a/src/editor/editor.d b/src/editor/editor.d index 91e2502..af8671b 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -145,19 +145,39 @@ Cycle(EditorCtx* ctx, Inputs* inputs) Vec4[4] col0 = Vec4(0.2, 0.4, 0.8, 1.0); Vec4[4] col1 = Vec4(0.8, 0.4, 0.2, 1.0); + Vec4[4] blue = Vec4(0.1, 0.3, 0.9, 1.0); Vec4[4] black = Vec4(0.0, 0.0, 0.0, 1.0); Vec4[4] green = Vec4(0.2, 0.8, 0.1, 1.0); + Vec4[4] white = Vec4(1.0); Push!("border_thickness", true)(ui_ctx, 10.0f); Push!("size_info")(ui_ctx, MakeUISizeX(ST.Percentage, 0.5)); Push!("bg_col", true)(ui_ctx, col0); UIItem* p0 = MakeItem("###p0", UIF.DrawBackground|UIF.Resizeable|UIF.DrawBorder); + Push!("layout_axis", true)(ui_ctx, A2D.Y); + Push!("parent", true)(ui_ctx, p0); + Push!("size_info", true)(ui_ctx, MakeUISize(UISize(ST.ChildrenSum, 1.0), UISize(ST.Percentage, 1.0))); + Push!("bg_col", true)(ui_ctx, blue); + UIItem* ln = MakeItem("###ln", UIF.DrawBackground); + + Push!("parent")(ui_ctx, ln); + Push!("bg_col")(ui_ctx, white); + Push!("padding")(ui_ctx, Vec2(4.0, 0.0)); + Push!("size_info")(ui_ctx, MakeUISize(UISize(ST.TextSize, 1.0), UISize(ST.Pixels, ui_ctx.char_height))); + + foreach(i; 0 .. 99) + { + MakeItem("%s##ln", i, UIF.DrawText); + } + + Pop!("padding", "size_info", "parent", "bg_col")(ui_ctx); + Push!("size_info", true)(ui_ctx, MakeUISize(UISize(ST.Pixels, 200.0), UISize(ST.Pixels, 80.0))); Push!("parent", true)(ui_ctx, p0); Push!("bg_col", true)(ui_ctx, green); Push!("padding", true)(ui_ctx, Vec2(4.0)); - UIItem* text = MakeItem("Haha##text", UIF.DrawBackground|UIF.DrawText); + UIItem* text = MakeItem("Haha0##text", UIF.DrawBackground|UIF.DrawText|UIF.RightAlignText|UIF.TextWrap); Push!("size_info", true)(ui_ctx, MakeUISizeX(ST.Pixels, 2.0)); Push!("bg_col", true)(ui_ctx, black); diff --git a/src/editor/ui.d b/src/editor/ui.d index ec6af2a..f1cd137 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -95,23 +95,26 @@ alias A2D = Axis2D; enum UIFlags { - None = 0, - DrawBackground = 1<<0, - DrawText = 1<<1, - DrawBorder = 1<<2, - Clickable = 1<<3, - Draggable = 1<<4, - ScrollX = 1<<5, - ScrollY = 1<<6, - ClampX = 1<<7, - ClampY = 1<<8, - Resizeable = 1<<9, - ResizeAdjacent = 1<<10, - FloatingWindow = 1<<11, - CenteredWindow = 1<<12, - TextInput = 1<<13, + None = 0, + DrawBackground = 1<<0, + DrawText = 1<<1, + DrawBorder = 1<<2, + Clickable = 1<<3, + Draggable = 1<<4, + ScrollX = 1<<5, + ScrollY = 1<<6, + ClampX = 1<<7, + ClampY = 1<<8, + Resizeable = 1<<9, + ResizeAdjacent = 1<<10, + FloatingWindow = 1<<11, + CenteredWindow = 1<<12, + TextInput = 1<<13, + RightAlignText = 1<<14, + CenterAlignText = 1<<15, + TextWrap = 1<<16, - Clamp = UIFlags.ClampX | UIFlags.ClampY, + Clamp = UIFlags.ClampX | UIFlags.ClampY, } alias UIF = UIFlags; @@ -128,12 +131,12 @@ alias UIS = UISignal; enum UIEvent { - None = 0, - Click = 1<<0, + None = 0, + Click = 1<<0, DragRelease = 1<<1, - Drag = 1<<2, - DragStart = 1<<3, - Press = 1<<4, + Drag = 1<<2, + DragStart = 1<<3, + Press = 1<<4, } alias UIE = UIEvent; @@ -214,6 +217,7 @@ struct UICtx FontFace font; FontAtlasBuf atlas_buf; f32 char_width; + f32 char_height; UIBuffer[FRAME_OVERLAP] buffers; @@ -300,7 +304,8 @@ struct UIItem Vec2 size; f32 resize_pct; - u8[] display_string; + string display_string; + string[] text_lines; mixin UIItemParameters!(); } @@ -310,6 +315,7 @@ enum SizeType Pixels, Percentage, ChildrenSum, + TextSize, } alias ST = SizeType; @@ -363,8 +369,8 @@ alias UIPair = KVPair!(UIHash, UIItem*); struct UIKey { - u8[] text, hash_text; - u64 hash; + string text, hash_text; + u64 hash; } struct UIPanel @@ -441,7 +447,8 @@ InitUICtx(PlatformWindow* window) UIItem* fi = ctx.free_items; fi.first = fi.last = fi.next = fi.prev = fi.parent = g_UI_NIL; - ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256); + ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256); + ctx.char_height = cast(f32)((ctx.font.size.metrics.ascender - ctx.font.size.metrics.descender) >> 6); version(ENABLE_RENDERER) { @@ -528,6 +535,26 @@ Set(UIItem* item, UICtx* ctx) } } +UIItem* +MakeItem(Args...)(string str, Args args) +{ + UIItem* item = g_UI_NIL; + + static if(Args.length) + { + enum has_flag = Args[Args.length-1].stringof == UIFlags.stringof; + + UIFlags flags = has_flag ? args[args.length-1] : UIF.None; + enum len = has_flag ? Args.length-1 : Args.length; + + char[] key = sformat(ScratchAlloc!(char)(cast(u64)(str.length*1.5)), str, args[0 .. len]); + + item = MakeItem(key, flags); + } + + return item; +} + UIItem* MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T) { @@ -560,9 +587,61 @@ MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T) } } - item.last_frame = ctx.frame; + string str = item.display_string.length ? item.display_string : item.key.text; + if(item.flags & UIF.TextWrap) + { + f32 width = item.size_info[A2D.X].type == ST.TextSize ? item.parent.size.x : item.size.x; + f32 text_width = CalcTextWidth(str); - item.padding += item.border_thickness; + if(text_width < width || width == 0.0) goto InitSingleLine; + + u64 lines = cast(u64)(ceil(text_width/width)); + item.text_lines = ScratchAlloc!(string)(lines); + + f32 w = 0.0; + u64 line = 0; + u64 ch_start = 0; + Glyph[] glyphs = ctx.atlas_buf.atlas.glyphs; + + for(u64 i = 0; i < str.length; i += 1) + { + Glyph* g = str[i] < glyphs.length ? glyphs.ptr+str[i] : glyphs.ptr+0; + + f32 glyph_w = GlyphWidth(g); + if(glyph_w+w > width) + { + item.text_lines[line++] = str[ch_start .. i]; + ch_start = i; + + if(line == lines-1) + { + item.text_lines[line] = str[ch_start .. $]; + break; + } + + w = 0.0; + } + + w += glyph_w; + } + } + else + { +InitSingleLine: + item.text_lines = ScratchAlloc!(string)(1); + item.text_lines[0] = str; + } + + item.last_frame = ctx.frame; + item.padding += item.border_thickness; + + if(item.text_lines.length > 1) + { + foreach(i; 0 .. item.text_lines.length) + { + Logf("line %s: %s", i, item.text_lines[i]); + } + } return item; } @@ -782,7 +861,18 @@ EndUI() { if(item.size_info[axis].type == ST.Pixels) { - item.size.v[axis] = item.size_info[axis].value; + item.size.v[axis] = item.padding.v[axis] + item.size_info[axis].value; + } + else if(item.size_info[axis].type == ST.TextSize) + { + static if(axis == A2D.X) + { + item.size.v[axis] = item.padding.x*2.0 + CalcTextWidth(item.display_string.length ? item.display_string : item.key.text); + } + else + { + item.size.v[axis] = item.padding.y*2.0 + TEXT_SIZE; + } } } @@ -812,10 +902,19 @@ EndUI() { if(item.size_info[axis].type == ST.ChildrenSum) { - f32 size = 0.0; - for(UIItem* c = item.first; !Nil(c); c = c.next) + if(item.layout_axis == axis) { - size += c.size.v[axis]; + for(UIItem* c = item.first; !Nil(c); c = c.next) + { + item.size.v[axis] += c.size.v[axis]; + } + } + else + { + for(UIItem* c = item.first; !Nil(c); c = c.next) + { + item.size.v[axis] = item.size.v[axis] < c.size.v[axis] ? c.size.v[axis] : item.size.v[axis]; + } } } } @@ -872,10 +971,7 @@ EndUI() { for(UIItem* c = item.first; !Nil(c); c = c.next) { - if(c.size.v[axis] > size) - { - c.size.v[axis] = size; - } + c.size.v[axis] = c.size.v[axis] > size ? size : c.size.v[axis]; } } } @@ -885,7 +981,16 @@ EndUI() f32 pos = 0.0; for(UIItem* item = ctx.root; !Nil(item);) { - f32 inner_pos = pos + item.parent.padding.v[axis]; + if(item.parent.layout_axis != axis) + { + pos = item.parent.rect.p0.v[axis] + item.parent.padding.v[axis]; + } + else if(item == item.parent.first) + { + pos += item.parent.padding.v[axis]; + } + + f32 inner_pos = pos; f32 next_pos = 0.0; f32 end_pos = inner_pos + item.size.v[axis]; @@ -925,7 +1030,6 @@ EndUI() } } - // Render Items RenderItems(ctx.root); RenderItems(ctx.window_root); @@ -962,8 +1066,8 @@ RenderItems(UIItem* root) { // DrawRect Vertex* v = GetVertex(ctx); - v.dst_start = item.rect.p0; // - item.border_thickness; - v.dst_end = item.rect.p1; // + item.border_thickness; + v.dst_start = item.rect.p0 + item.border_thickness; + v.dst_end = item.rect.p1 - item.border_thickness; v.cols = item.bg_col; v.corner_radius = item.corner_radius; @@ -983,16 +1087,28 @@ RenderItems(UIItem* root) AddVertexCount(ctx); } - if(item.flags & UIF.DrawText) + if(item.flags & UIF.DrawText || item.display_string) { - FontAtlas* atl = &ctx.atlas_buf.atlas; - f32 x_pos = item.rect.p0.x + item.border_thickness + item.padding.x; - f32 y_pos = item.rect.p0.y + item.border_thickness + item.padding.y; - foreach(i; 0 .. item.key.text.length) + string str = item.display_string ? item.display_string : item.key.text; + + if(item.flags & UIF.CenterAlignText) { - u8 ch = item.key.text[i]; - Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null; - DrawGlyph(item, g, &x_pos, y_pos); + // TODO + } + else + { + FontAtlas* atl = &ctx.atlas_buf.atlas; + + f32 x_pos = item.flags & UIF.RightAlignText ? item.rect.p1.x - item.padding.x - CalcTextWidth(str) : + item.rect.p0.x + item.padding.x; + + f32 y_pos = item.rect.p0.y + item.padding.y; + foreach(i; 0 .. str.length) + { + u8 ch = str[i]; + Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null; + DrawGlyph(item, g, &x_pos, y_pos); + } } } } @@ -1072,6 +1188,15 @@ Pop(string stack_str)(UICtx* ctx) return pop.value; } +void +Pop(stack_strs...)(UICtx* ctx) +{ + static foreach(stack; stack_strs) + { + Pop!(stack)(ctx); + } +} + void AutoPopStacks(UICtx* ctx) { @@ -1225,11 +1350,11 @@ MakeKey(T)(T str) if(StringType!T) { static if(is(T: string)) { - u8[] id = CastStr!(u8)(str); + string id = str; } else { - u8[] id = cast(u8[])str; + string id = ConvToStr(str); } UIKey key; @@ -1264,8 +1389,8 @@ MakeKey(T)(T str) if(StringType!T) if(hash_count < 2 || hash_only) { - key.text = hash_only ? [] : id; - key.hash_text = hash_only ? id : []; + key.text = hash_only ? "" : id; + key.hash_text = hash_only ? id : ""; key.hash = Hash(id); } else if(hash_count == 2 || hash_count == 3) @@ -1313,7 +1438,7 @@ Get(T)(T k) if(is(T: UIKey) || StringType!T) } f32 -CalcTextWidth(u8[] str) +CalcTextWidth(string str) { u32 tab_width = g_ui_ctx.tab_width; Glyph* space = g_ui_ctx.atlas_buf.atlas.glyphs.ptr + ' '; @@ -1363,20 +1488,18 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y) f32 b = glyph.plane_bottom * item.text_scale; GlyphBounds gb = { - r: r, - l: l, - t: t, - b: b, - w: r - l, - h: b - t, - atlas_r: glyph.atlas_right, - atlas_l: glyph.atlas_left, - atlas_t: glyph.atlas_top, - atlas_b: glyph.atlas_bottom, + r: r, + l: l, + t: t, + b: b, + w: r - l, + h: b - t, + atlas_r: glyph.atlas_right, + atlas_l: glyph.atlas_left, + atlas_t: glyph.atlas_top, + atlas_b: glyph.atlas_bottom, }; - f32 y_pos = t; - v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; v.dst_start = Vec2(*x_pos+gb.l, y); @@ -1392,15 +1515,22 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y) f32 end_x = *x_pos + advance; f32 width = end_x - *x_pos; - if(end_x > item.rect.p1.x) + f32 bound_x = item.flags & UIF.RightAlignText ? item.rect.p0.x : item.rect.p1.x; + if(item.flags & UIF.RightAlignText && bound_x > *x_pos) { - f32 cull_pct = (end_x - item.rect.p1.x) / width; + f32 cull_pct = (bound_x - *x_pos) / width; + v.dst_start.x += (v.dst_end.x - v.dst_start.x) * cull_pct; + v.src_start.x += (v.src_end.x - v.src_start.x) * cull_pct; + } + else if(!(item.flags & UIF.RightAlignText) && end_x > bound_x) + { + f32 cull_pct = (end_x - bound_x) / width; v.dst_end.x -= (v.dst_end.x - v.dst_start.x) * cull_pct; v.src_end.x -= (v.src_end.x - v.src_start.x) * cull_pct; } - f32 end_y = y_pos + TEXT_SIZE; - f32 height = end_y - y_pos; + f32 end_y = y + TEXT_SIZE; + f32 height = end_y - y; if(end_y > item.rect.p1.y) { f32 cull_pct = (end_y - item.rect.p1.y) / height;