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 }