1 module rpui.widgets.tree_list_node.renderer;
2 
3 import std.container.array;
4 import std.math;
5 
6 import gapi.texture;
7 
8 import rpui.basic_rpdl_exts;
9 import rpui.primitives;
10 import rpui.theme;
11 import rpui.events;
12 import rpui.widget;
13 import rpui.math;
14 import rpui.widgets.button.renderer;
15 import rpui.widgets.tree_list_node.widget;
16 import rpui.render.components;
17 import rpui.render.components_factory;
18 import rpui.render.renderer;
19 import rpui.render.transforms;
20 
21 final class TreeListNodeRenderer : ButtonRenderer {
22     private TreeListNode widget;
23     private Theme theme;
24     private const string treeListStyle;
25 
26     private vec4 linesColor;
27     private float lineLength;
28     private float rootLineLength;
29     private float enterExpandButtonAlpha;
30     private float leaveExpandButtonAlpha;
31     private vec2 expandButtonOffset;
32     private FrameRect expandButtonBorders;
33 
34     private LinesGeometry lines;
35     private Array!vec2 vertices;
36     private Array!uint indices;
37     private LinesTransforms linesTransforms;
38     private TextureQuad expandButton;
39     private OriginalWithNormilizedTextureCoords expandButtonOpenTexCoords;
40     private OriginalWithNormilizedTextureCoords expandButtonCloseTexCoords;
41     private QuadTransforms expandButtonTransforms;
42 
43     private bool needUpdateLines = true;
44 
45     this(in string treeListStyle) {
46         this.treeListStyle = treeListStyle;
47     }
48 
49     override void onCreate(Widget widget, in string style) {
50         super.onCreate(widget, style);
51 
52         this.theme = widget.view.theme;
53         this.widget = cast(TreeListNode) widget;
54 
55         auto data = theme.tree.data;
56 
57         expandButton = createUiSkinTextureQuad(theme);
58         expandButtonOpenTexCoords = createOriginalWithNormilizedTextureCoordsFromRdpl(
59             theme,
60             style ~ ".openIcon"
61         );
62         expandButtonCloseTexCoords = createOriginalWithNormilizedTextureCoordsFromRdpl(
63             theme,
64             style ~ ".closeIcon"
65         );
66 
67         linesColor = data.getNormColor(treeListStyle ~ ".linesColor");
68         lineLength = data.getNumber(treeListStyle ~ ".lineLength.0");
69         rootLineLength = data.getNumber(treeListStyle ~ ".rootLineLength.0");
70         enterExpandButtonAlpha = data.getNumber(style ~ ".enterExpandButtonAlpha.0");
71         leaveExpandButtonAlpha = data.getNumber(style ~ ".leaveExpandButtonAlpha.0");
72         expandButtonOffset = data.getVec2f(style ~ ".expandButtonOffset");
73         expandButtonBorders = data.getFrameRect(style ~ ".expandButtonBorders");
74 
75         vertices.reserve(4);
76         indices.reserve(4);
77 
78         vertices.insert(vec2(0.0f, 0.0f));
79         vertices.insert(vec2(0.0f, 0.0f));
80         vertices.insert(vec2(0.0f, 0.0f));
81         vertices.insert(vec2(0.0f, 0.0f));
82 
83         indices.insert(0);
84         indices.insert(1);
85         indices.insert(2);
86         indices.insert(3);
87 
88         lines = createDynamicLinesGeometry(vertices, indices);
89     }
90 
91     override void onRender() {
92         super.onRender();
93 
94         if (widget.treeDepth <= 2) {
95             renderColorLines(theme, lines, linesColor, linesTransforms);
96         }
97 
98         if (widget.isOpen) {
99             widget.renderChildren();
100         }
101 
102         renderExpandButton();
103     }
104 
105     private void renderExpandButton() {
106         if (widget.children.empty) {
107             return;
108         }
109 
110         Texture2DCoords expandButtonTexCoord;
111 
112         if (widget.isOpen) {
113             expandButtonTexCoord = expandButtonOpenTexCoords.normilizedTexCoords;
114         } else {
115             expandButtonTexCoord = expandButtonCloseTexCoords.normilizedTexCoords;
116         }
117 
118         renderTexAtlasQuad(
119             theme,
120             expandButton.geometry,
121             expandButton.texture,
122             expandButtonTexCoord,
123             expandButtonTransforms,
124             widget.isExpandButtonEnter ? enterExpandButtonAlpha : leaveExpandButtonAlpha
125         );
126     }
127 
128     override void onProgress(in ProgressEvent event) {
129         super.onProgress(event);
130 
131         if (widget.treeDepth <= 2) {
132             linesTransforms = updateLinesTransforms(widget.view.cameraView, widget.absolutePosition);
133             updateLines();
134         }
135 
136         updateExpandButton();
137     }
138 
139     private void updateExpandButton() {
140         const length = widget.parent == widget.treeList ? rootLineLength : lineLength;
141 
142         const position = widget.absolutePosition + vec2(-length, 0) + expandButtonOffset;
143         const size = expandButtonOpenTexCoords.originalTexCoords.size;
144 
145         expandButtonTransforms = updateQuadTransforms(
146             widget.view.cameraView,
147             position,
148             size
149         );
150 
151         widget.isExpandButtonEnter = pointInRect(
152             widget.view.mousePos,
153             Rect(position, size)
154         );
155     }
156 
157     private void updateLines() {
158         with (widget) {
159             const length = parent == treeList ? rootLineLength : lineLength;
160             const isFirst = widget == parent.children.front;
161             const halfHeight = round(height / 2.0f) - 1.0f;  // -1 due-to line height
162             const currentExpandButtonBorders = widget.children.empty
163                 ? FrameRect()
164                 : expandButtonBorders;
165             const currentPrevWidgetExpandButtonBorders = prevWidget.children.empty
166                 ? FrameRect()
167                 : expandButtonBorders;
168 
169             if (treeDepth == 2) {
170                 const deltaBeetwenNodes = absolutePosition.y - prevWidget.absolutePosition.y;
171 
172                 // NOTE(Andrey): -1 it's a just adjustment for root node line.
173                 const top = isFirst ? halfHeight - 1.0f : deltaBeetwenNodes;
174 
175                 vertices.clear();
176                 vertices.insert(vec2(0.0f, -halfHeight));
177                 vertices.insert(vec2(-length + currentExpandButtonBorders.right, -halfHeight));
178                 vertices.insert(vec2(-length, -halfHeight + currentExpandButtonBorders.top));
179                 vertices.insert(vec2(-length, -halfHeight + top - currentPrevWidgetExpandButtonBorders.bottom));
180 
181                 indices.clear();
182                 indices.insert(0);
183                 indices.insert(1);
184                 indices.insert(2);
185                 indices.insert(3);
186             } else {
187                 vertices.clear();
188                 vertices.insert(vec2(0.0f, -halfHeight));
189                 vertices.insert(vec2(-length + currentExpandButtonBorders.right, -halfHeight));
190 
191                 indices.clear();
192                 indices.insert(0);
193                 indices.insert(1);
194             }
195 
196             updateLinesGeometryData(lines, vertices, indices);
197             needUpdateLines = false;
198         }
199     }
200 }