1 module rpui.widgets.text_input.transforms_system; 2 3 import std.container.array; 4 import std.math; 5 6 import rpui.events; 7 import rpui.widgets.text_input.widget; 8 import rpui.render.components_factory; 9 import rpui.render.components; 10 import rpui.render.transforms; 11 import rpui.theme; 12 import rpui.widgets.text_input.render_system; 13 import rpui.math; 14 import rpui.primitives; 15 import rpui.widget; 16 17 struct RenderTransforms { 18 vec2 focusOffsets; 19 float focusResize; 20 float selectRegionHeight; 21 vec2 selectRegionOffset; 22 vec2 arrowOffsets; 23 float prefixMargin; 24 float postfixMargin; 25 float softPostfixMargin; 26 float arrowAreaSize; 27 float softPostfixWidth; 28 29 HorizontalChainTransforms background; 30 HorizontalChainTransforms focusGlow; 31 QuadTransforms carriage; 32 UiTextTransforms text; 33 UiTextTransforms prefix; 34 UiTextTransforms postfix; 35 QuadTransforms selectRegion; 36 QuadTransforms leftArrow; 37 QuadTransforms rightArrow; 38 } 39 40 final class TextInputTransformsSystem : TransformsSystem { 41 private RenderTransforms* transforms; 42 private TextInput widget; 43 private Theme theme; 44 private RenderData* renderData; 45 46 this(TextInput widget, RenderData* renderData, RenderTransforms* transforms) { 47 this.widget = widget; 48 this.theme = widget.view.theme; 49 this.renderData = renderData; 50 this.transforms = transforms; 51 } 52 53 override void onProgress(in ProgressEvent event) { 54 updateBackground(); 55 updateCarriage(); 56 updateTextPosition(); 57 updatePrefix(); 58 updatePostfix(); 59 updateText(); 60 updateSoftPostfix(); 61 updateSelectRegion(); 62 updateArrows(); 63 } 64 65 private void updateBackground() { 66 transforms.background = updateHorizontalChainTransforms( 67 renderData.background.widths, 68 widget.view.cameraView, 69 widget.absolutePosition, 70 widget.size, 71 widget.partDraws 72 ); 73 74 if (widget.focusable && widget.isFocused) { 75 transforms.focusGlow = updateHorizontalChainTransforms( 76 renderData.background.widths, 77 widget.view.cameraView, 78 widget.absolutePosition + transforms.focusOffsets, 79 widget.size + vec2(transforms.focusResize), 80 widget.partDraws 81 ); 82 } 83 } 84 85 private void updateCarriage() { 86 transforms.carriage = updateQuadTransforms( 87 widget.view.cameraView, 88 widget.editComponent.carriage.absolutePosition, 89 renderData.carriage.texCoords.originalTexCoords.size 90 ); 91 } 92 93 private void updateArrows() { 94 if (widget.isNumberMode()) { 95 transforms.arrowAreaSize = widget.measure.arrowsAreaWidth; 96 } else { 97 transforms.arrowAreaSize = 0; 98 } 99 100 renderData.leftArrow.state = widget.numberInputTypeComponent.leftArrow.state; 101 renderData.rightArrow.state = widget.numberInputTypeComponent.rightArrow.state; 102 103 updateArrowAbsolutePositions(); 104 105 transforms.leftArrow = updateQuadTransforms( 106 widget.view.cameraView, 107 widget.numberInputTypeComponent.leftArrow.absolutePosition, 108 renderData.leftArrow.currentTexCoords.originalTexCoords.size 109 ); 110 111 transforms.rightArrow = updateQuadTransforms( 112 widget.view.cameraView, 113 widget.numberInputTypeComponent.rightArrow.absolutePosition, 114 renderData.rightArrow.currentTexCoords.originalTexCoords.size 115 ); 116 } 117 118 private void updateArrowAbsolutePositions() { 119 with (widget.numberInputTypeComponent) { 120 leftArrow.absolutePosition = widget.absolutePosition + transforms.arrowOffsets; 121 122 const rightArrowWidth = renderData.rightArrow.currentTexCoords.originalTexCoords.size.x; 123 const rightArrowOffsets = vec2( 124 widget.size.x - rightArrowWidth - transforms.arrowOffsets.x, 125 transforms.arrowOffsets.y 126 ); 127 128 rightArrow.absolutePosition = widget.absolutePosition + rightArrowOffsets; 129 } 130 } 131 132 private void updateText() { 133 with (renderData.text.attrs[widget.state]) { 134 caption = widget.editComponent.text; 135 textAlign = widget.textAlign; 136 } 137 138 vec2 textOffset = vec2(0); 139 140 if (widget.textAlign == Align.left) { 141 textOffset.x += widget.measure.prefixWidth; 142 } 143 144 if (widget.textAlign == Align.right) { 145 textOffset.x -= widget.measure.postfixWidth; 146 } 147 148 if (widget.softPostfix && widget.textAlign == Align.center) { 149 textOffset.x -= transforms.softPostfixWidth / 2; 150 } 151 152 if (widget.softPostfix && widget.textAlign == Align.right) { 153 textOffset.x -= transforms.softPostfixWidth + transforms.softPostfixMargin; 154 } 155 156 transforms.text = updateUiTextTransforms( 157 &renderData.text.render, 158 &theme.regularFont, 159 transforms.text, 160 renderData.text.attrs[widget.state], 161 widget.view.cameraView, 162 widget.editComponent.absoulteTextPosition + textOffset, 163 widget.size 164 ); 165 166 widget.measure.textWidth = transforms.text.size.x; 167 widget.measure.lineHeight = transforms.text.size.y; 168 widget.measure.textRelativePosition = vec2(transforms.text.relativePosition.x, 0) + textOffset; 169 } 170 171 private void updatePrefix() { 172 if (widget.prefix == "") { 173 widget.measure.prefixWidth = 0; 174 return; 175 } 176 177 with (renderData.prefix.attrs[widget.state]) { 178 caption = widget.prefix; 179 textAlign = Align.left; 180 } 181 182 transforms.prefix = updateUiTextTransforms( 183 &renderData.prefix.render, 184 &theme.regularFont, 185 transforms.prefix, 186 renderData.prefix.attrs[widget.state], 187 widget.view.cameraView, 188 widget.absolutePosition + vec2(widget.measure.textLeftMargin + transforms.arrowAreaSize, 0), 189 widget.size 190 ); 191 192 widget.measure.prefixWidth = transforms.prefix.size.x + transforms.prefixMargin + 193 transforms.arrowAreaSize; 194 } 195 196 private void updatePostfix() { 197 if (widget.postfix == "" || widget.softPostfix) { 198 widget.measure.postfixWidth = 0; 199 return; 200 } 201 202 with (renderData.postfix.attrs[widget.state]) { 203 caption = widget.postfix; 204 textAlign = Align.right; 205 } 206 207 transforms.postfix = updateUiTextTransforms( 208 &renderData.postfix.render, 209 &theme.regularFont, 210 transforms.postfix, 211 renderData.postfix.attrs[widget.state], 212 widget.view.cameraView, 213 widget.absolutePosition - vec2(widget.measure.textRightMargin + transforms.arrowAreaSize, 0), 214 widget.size 215 ); 216 217 widget.measure.postfixWidth = transforms.postfix.size.x + transforms.postfixMargin + 218 transforms.arrowAreaSize; 219 } 220 221 private void updateSoftPostfix() { 222 if (widget.postfix == "" || !widget.softPostfix) { 223 return; 224 } 225 226 with (renderData.postfix.attrs[widget.state]) { 227 caption = widget.postfix; 228 textAlign = Align.left; 229 } 230 231 float textOffset = widget.measure.textWidth + transforms.softPostfixMargin + 232 transforms.text.relativePosition.x; 233 234 switch (widget.textAlign) { 235 case Align.left: 236 textOffset += widget.measure.prefixWidth; 237 break; 238 239 case Align.right: 240 textOffset -= transforms.softPostfixWidth + transforms.softPostfixMargin; 241 break; 242 243 case Align.center: 244 textOffset -= transforms.softPostfixWidth / 2; 245 break; 246 247 default: 248 break; 249 } 250 251 transforms.postfix = updateUiTextTransforms( 252 &renderData.postfix.render, 253 &theme.regularFont, 254 transforms.postfix, 255 renderData.postfix.attrs[widget.state], 256 widget.view.cameraView, 257 widget.editComponent.absoulteTextPosition + vec2(textOffset, 0), 258 widget.size 259 ); 260 261 transforms.softPostfixWidth = transforms.postfix.size.x; 262 widget.measure.postfixWidth = 0; 263 } 264 265 private void updateTextPosition() { 266 auto textPosition = widget.absolutePosition; 267 268 if (widget.textAlign == Align.left) { 269 textPosition.x += widget.measure.textLeftMargin + widget.editComponent.scrollDelta; 270 } 271 else if (widget.textAlign == Align.right) { 272 textPosition.x -= widget.measure.textRightMargin - widget.editComponent.scrollDelta; 273 } 274 275 widget.editComponent.absoulteTextPosition = textPosition; 276 } 277 278 package float getRegionTextWidth(in int start, in int end) { 279 auto attrs = renderData.text.attrs[widget.state]; 280 attrs.caption = widget.editComponent.text[start .. end]; 281 return getUiTextBounds(&renderData.text.render, &theme.regularFont, attrs).x; 282 } 283 284 private void updateSelectRegion() { 285 with (widget.editComponent) { 286 if (!selectRegion.textIsSelected()) 287 return; 288 289 selectRegion.clampSelectRegion(); 290 const regionSize = getRegionTextWidth(selectRegion.start, selectRegion.end); 291 292 selectRegion.size = vec2(regionSize, transforms.selectRegionHeight); 293 selectRegion.absolutePosition = widget.absolutePosition + 294 getTextRegionOffset(selectRegion.start) + 295 transforms.selectRegionOffset; 296 297 transforms.selectRegion = updateQuadTransforms( 298 widget.view.cameraView, 299 selectRegion.absolutePosition, 300 selectRegion.size 301 ); 302 } 303 } 304 }