1 module rpui.widgets.chain_layout.widget;
2 
3 import std.math;
4 
5 import rpui.events;
6 import rpui.primitives;
7 import rpui.math;
8 import rpui.widget;
9 import rpui.widgets.stack_layout.stack_locator;
10 import rpui.widgets.chain_layout.renderer;
11 
12 final class ChainLayout : Widget {
13     private StackLocator stackLocator;
14 
15     this(in string style = "ChainLayout") {
16         super(style);
17 
18         skipFocus = true;
19         stackLocator.attach(this);
20         stackLocator.orientation = Orientation.horizontal;
21         renderer = new ChainLayoutRenderer();
22     }
23 
24     override void onProgress(in ProgressEvent event) {
25         super.onProgress(event);
26 
27         locator.updateLocationAlign();
28         locator.updateVerticalLocationAlign();
29         locator.updateRegionAlign();
30         locator.updateAbsolutePosition();
31 
32         updateSize();
33 
34         foreach (Widget widget; children) {
35             updatePartDraws(widget, PartDraws.center);
36         }
37 
38         updatePartDraws(children.front, PartDraws.left);
39         updatePartDraws(children.back, PartDraws.right);
40 
41         // little adjustment.
42         // TODO(Andrey): explay meaning of number 1
43         children.back.associatedWidget.margin.left = 1;
44     }
45 
46     override void updateSize() {
47         super.updateSize();
48 
49         handleWidgetsVariableSize();
50         stackLocator.updateWidgetsPosition();
51         stackLocator.updateSize();
52     }
53 
54     private void handleWidgetsVariableSize() {
55         float fixedSize = 0;
56         int nonFixedCount = 0;
57 
58         if (widthType == SizeType.matchParent) {
59             locationAlign = Align.none;
60             size.x = parent.innerSize.x - outerOffsetSize.x;
61             position.x = 0;
62         }
63 
64         foreach (Widget row; children) {
65             const widget = row.associatedWidget;
66 
67             if (!widget.isVisible) {
68                 continue;
69             }
70 
71             if (widget.widthType != SizeType.matchParent) {
72                 fixedSize += widget.size.x;
73             } else {
74                 nonFixedCount += 1;
75             }
76         }
77 
78         const partWidth = round((size.x - fixedSize) / nonFixedCount);
79 
80         float total = 0;
81         Widget lastNonFixedWidget = null;
82 
83         foreach (Widget row; children) {
84             Widget widget = row.associatedWidget;
85 
86             if (!widget.isVisible) {
87                 continue;
88             }
89 
90             if (widget.widthType == SizeType.matchParent) {
91                 widget.size.x = partWidth;
92                 lastNonFixedWidget = widget;
93             }
94 
95             total += widget.width;
96         }
97 
98         // NOTE(Andrey): Adjustment because of partWidth rounding.
99         if (lastNonFixedWidget !is null) {
100             lastNonFixedWidget.size.x += size.x - total - 1;
101         }
102     }
103 
104     private void updatePartDraws(Widget row, in Widget.PartDraws partDraws) {
105         row.associatedWidget.partDraws = partDraws;
106     }
107 
108     override void onRender() {
109         renderer.onRender();
110     }
111 }