1 module rpui.widgets.multiline_label.renderer; 2 3 import std.container.array; 4 import std..string; 5 import std.array; 6 import std.math; 7 8 import rpui.math; 9 import rpui.primitives; 10 import rpui.theme; 11 import rpui.events; 12 import rpui.widget; 13 import rpui.widgets.multiline_label.widget; 14 import rpui.render.components; 15 import rpui.render.components_factory; 16 import rpui.render.renderer; 17 import rpui.render.transforms; 18 19 final class MultilineLabelRenderer : Renderer { 20 private Array!UiText lines; 21 private Array!UiTextTransforms textTransforms; 22 private MultilineLabel widget; 23 private Theme theme; 24 private utf32string lastCaption; 25 26 override void onCreate(Widget widget, in string style) { 27 this.theme = widget.view.theme; 28 this.widget = cast(MultilineLabel) widget; 29 } 30 31 override void onRender() { 32 for (size_t i = 0; i < lines.length; ++i) { 33 const transforms = textTransforms[i]; 34 const text = lines[i]; 35 renderUiText(theme, text.render, text.attrs, transforms); 36 } 37 } 38 39 override void onProgress(in ProgressEvent event) { 40 if (lastCaption != widget.caption) { 41 updateLines(); 42 lastCaption = widget.caption; 43 } 44 45 const textLineHeight = widget.measure.lineHeight * widget.lineHeightFactor; 46 float textCurrentPosY = getStartTextPosY(textLineHeight); 47 48 widget.measure.maxLineWidth = 0; 49 widget.measure.lineHeight = 0; 50 51 for (size_t i = 0; i < lines.length; ++i) { 52 const textPos = vec2(widget.innerOffsetStart.x + widget.absolutePosition.x, textCurrentPosY); 53 const textSizeY = textLineHeight; 54 const textSize = vec2(widget.size.x - widget.innerOffsetSize.x, textSizeY); 55 textCurrentPosY += textSizeY; 56 57 with (lines[i].attrs) { 58 textAlign = widget.textAlign; 59 textVerticalAlign = widget.textVerticalAlign; 60 } 61 62 textTransforms[i] = updateUiTextTransforms( 63 &lines[i].render, 64 &theme.regularFont, 65 textTransforms[i], 66 lines[i].attrs, 67 widget.view.cameraView, 68 textPos, 69 textSize 70 ); 71 72 if (widget.measure.maxLineWidth < textTransforms[i].size.x) { 73 widget.measure.maxLineWidth = textTransforms[i].size.x; 74 } 75 76 if (widget.measure.lineHeight < textTransforms[i].size.y) { 77 widget.measure.lineHeight = textTransforms[i].size.y; 78 } 79 } 80 81 widget.measure.linesCount = lines.length; 82 } 83 84 private float getStartTextPosY(in float textLineHeight) { 85 const boundaryHeight = textLineHeight * lines.length; 86 float textPosY = widget.absolutePosition.y; 87 88 switch (widget.textVerticalAlign) { 89 case VerticalAlign.bottom: 90 textPosY += widget.size.y - boundaryHeight - widget.innerOffsetEnd.y; 91 break; 92 93 case VerticalAlign.middle: 94 textPosY += round((widget.size.y - boundaryHeight) * 0.5); 95 break; 96 97 default: 98 textPosY += widget.innerOffsetStart.y; 99 break; 100 } 101 102 return textPosY; 103 } 104 105 private void updateLines() { 106 textTransforms.clear(); 107 const strings = lineSplitter(widget.caption).array; 108 109 lines.length = strings.length; 110 textTransforms.length = strings.length; 111 112 for (size_t i = 0; i < lines.length; ++i) { 113 lines[i] = createUiTextFromRdpl(theme, widget.style, "Regular"); 114 lines[i].attrs.caption = strings[i]; 115 } 116 } 117 }