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 }