1 module rpui.widgets.button.transforms_system;
2 
3 import std.container.array;
4 import std.math;
5 import std.algorithm;
6 
7 import rpui.events;
8 import rpui.widgets.button.widget;
9 import rpui.render.components_factory;
10 import rpui.render.components;
11 import rpui.render.transforms;
12 import rpui.theme;
13 import rpui.widgets.button.render_system;
14 import rpui.math;
15 import rpui.primitives;
16 import rpui.widget;
17 
18 struct RenderTransforms {
19     HorizontalChainTransforms background;
20     HorizontalChainTransforms focusGlow;
21     UiTextTransforms captionText;
22     Array!QuadTransforms icons;
23 }
24 
25 final class ButtonTransformsSystem : TransformsSystem {
26     private RenderTransforms* transforms;
27     private Button widget;
28     private Theme theme;
29     private RenderData* renderData;
30 
31     this(Button widget, RenderData* renderData, RenderTransforms* transforms) {
32         this.widget = widget;
33         this.theme = widget.view.theme;
34         this.renderData = renderData;
35         this.transforms = transforms;
36     }
37 
38     override void onProgress(in ProgressEvent event) {
39         updateBackground();
40         updateText();
41         updateIcons();
42     }
43 
44     private void updateBackground() {
45         const uselseeBorderSize = vec2(
46             widget.measure.uselessBorders.left + widget.measure.uselessBorders.right,
47             0
48             // widget.measure.uselessBorders.top + widget.measure.uselessBorders.bottom
49         );
50 
51         transforms.background = updateHorizontalChainTransforms(
52             renderData.background.widths,
53             widget.view.cameraView,
54             widget.absolutePosition - vec2(widget.measure.uselessBorders.left, -widget.measure.uselessBorders.bottom),
55             widget.size + uselseeBorderSize,
56             widget.partDraws
57         );
58 
59         if (widget.focusable && widget.isFocused) {
60             transforms.focusGlow = updateHorizontalChainTransforms(
61                 renderData.background.widths,
62                 widget.view.cameraView,
63                 widget.absolutePosition + widget.measure.focusOffsets,
64                 widget.size + vec2(widget.measure.focusResize),
65                 widget.partDraws
66             );
67         }
68     }
69 
70     private void updateText() {
71         if (widget.caption == "") {
72             widget.measure.textWidth = 0;
73             return;
74         }
75 
76         const captionTransforms = getCaptonTransforms(widget.textAlign);
77 
78         with (renderData.captionText.attrs[widget.state]) {
79             caption = widget.caption;
80             textAlign = widget.textAlign;
81             textVerticalAlign = widget.textVerticalAlign;
82         }
83 
84         transforms.captionText = updateUiTextTransforms(
85             &renderData.captionText.render,
86             &theme.regularFont,
87             transforms.captionText,
88             renderData.captionText.attrs[widget.state],
89             widget.view.cameraView,
90             captionTransforms.position,
91             captionTransforms.size
92         );
93 
94         widget.measure.textWidth = transforms.captionText.size.x;
95     }
96 
97     struct CaptionTransforms {
98         vec2 position;
99         vec2 size;
100     }
101 
102     CaptionTransforms getCaptonTransforms(in Align textAlign) {
103         const textBoxSize = widget.size - vec2(widget.measure.iconsAreaSize, 0);
104         auto textPosition = vec2(widget.measure.iconsAreaSize, 0) + widget.absolutePosition;
105 
106         if (textAlign == Align.left) {
107             textPosition.x += widget.measure.textLeftMargin;
108         }
109         else if (textAlign == Align.right) {
110             textPosition.x -= widget.measure.textRightMargin;
111         }
112 
113         if (widget.partDraws == Widget.PartDraws.left || widget.partDraws == Widget.PartDraws.right) {
114             textPosition.x -= 1;
115         }
116 
117         return CaptionTransforms(
118             textPosition,
119             textBoxSize
120         );
121     }
122 
123     private void updateIcons() {
124         with (widget) {
125             measure.iconsAreaSize = 0;
126 
127             const iconSize = view.resources.icons.getIconsConfig(iconsGroup).size;
128             const iconVerticalOffset = round((size.y - iconSize.y) / 2.0f);
129             float iconLastOffset = 0;
130             int counter = 0;
131 
132             for (int i = 0; i < widget.icons.length; ++i) {
133                 const iconOffset = iconLastOffset;
134 
135                 iconLastOffset = iconOffset + iconSize.x + measure.iconGaps;
136                 counter++;
137                 const offset = measure.iconOffsets + vec2(iconOffset, iconVerticalOffset);
138 
139                 const transform = updateQuadTransforms(
140                     view.cameraView,
141                     absolutePosition + offset,
142                     iconSize
143                 );
144                 transforms.icons[i] = transform;
145             }
146 
147             if (icons.length > 0) {
148                 measure.iconsAreaSize += iconLastOffset - measure.iconGaps * 2;
149             }
150 
151             if (widget.uselessIconArea != 0) {
152                 const count = max(icons.length, widget.uselessIconArea);
153                 measure.iconsAreaSize = (iconSize.x + measure.iconGaps) * count - measure.iconGaps * 2;
154             }
155         }
156     }
157 }