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.widthType != SizeType.matchParent) {
68                 fixedSize += widget.size.x;
69             } else {
70                 nonFixedCount += 1;
71             }
72         }
73 
74         const partWidth = round((size.x - fixedSize) / nonFixedCount);
75 
76         float total = 0;
77         Widget lastNonFixedWidget = null;
78 
79         foreach (Widget row; children) {
80             Widget widget = row.associatedWidget;
81 
82             if (widget.widthType == SizeType.matchParent) {
83                 widget.size.x = partWidth;
84                 lastNonFixedWidget = widget;
85             }
86 
87             total += widget.width;
88         }
89 
90         // NOTE(Andrey): Adjustment because of partWidth rounding.
91         if (lastNonFixedWidget !is null) {
92             lastNonFixedWidget.size.x += size.x - total - 1;
93         }
94     }
95 
96     private void updatePartDraws(Widget row, in Widget.PartDraws partDraws) {
97         row.associatedWidget.partDraws = partDraws;
98     }
99 
100     override void onRender() {
101         renderer.onRender();
102     }
103 }