1 // Written in the D programming language.
2 
3 /**
4 This module contains Group Box widget implementation.
5 
6 Group box is linear layout with frame and caption for grouping controls.
7 
8 
9 Synopsis:
10 
11 ----
12 import dlangui.widgets.groupbox;
13 
14 ----
15 
16 Copyright: Vadim Lopatin, 2016
17 License:   Boost License 1.0
18 Authors:   Vadim Lopatin, coolreader.org@gmail.com
19 */
20 module dlangui.widgets.groupbox;
21 
22 import dlangui.widgets.widget;
23 import dlangui.widgets.layouts;
24 
25 class GroupBox : LinearLayout {
26     import dlangui.widgets.controls;
27 
28     protected TextWidget _caption;
29 
30     this() {
31         this(null, ""d, Orientation.Vertical);
32     }
33 
34     this(string ID, UIString uitext, Orientation orientation = Orientation.Vertical) {
35         super(ID, orientation);
36         styleId = STYLE_GROUP_BOX;
37         _caption = new TextWidget("GROUP_BOX_CAPTION");
38         _caption.styleId = STYLE_GROUP_BOX_CAPTION;
39         _caption.parent = this;
40         text = uitext;
41     }
42 
43     this(string ID, string textResourceId, Orientation orientation = Orientation.Vertical) {
44         this(ID, UIString.fromId(textResourceId), orientation);
45     }
46 
47     this(string ID, dstring rawText, Orientation orientation = Orientation.Vertical) {
48         this(ID, UIString.fromRaw(rawText), orientation);
49     }
50 
51     ~this() {
52         destroy(_caption);
53     }
54 
55     /// get widget text
56     override @property dstring text() const { return _caption.text; }
57     /// set text to show
58     override @property Widget text(dstring s) {
59         _caption.text = s;
60         requestLayout();
61         return this;
62     }
63     /// set text to show
64     override @property Widget text(UIString s) {
65         _caption.text = s;
66         requestLayout();
67         return this;
68     }
69     /// set text resource ID to show
70     @property Widget textResource(string s) {
71         _caption.textResource = s;
72         requestLayout();
73         return this;
74     }
75 
76     int _topFrameHeight;
77     int _topFrameLeft;
78     int _topFrameRight;
79     int _captionHeight;
80     int _topHeight;
81     int _frameLeft;
82     int _frameRight;
83     int _frameBottom;
84     int _frameWidth;
85     int _frameHeight;
86     protected void calcFrame() {
87         Rect captPadding = _caption.padding;
88         Rect captMargins = _caption.margins;
89         int captFontHeight = _caption.font.height;
90         _captionHeight = captPadding.top + captPadding.bottom + captMargins.top + captMargins.bottom + captFontHeight;
91         _topFrameHeight = 0;
92         DrawableRef upLeftDrawable = style.customDrawable("group_box_frame_up_left");
93         DrawableRef upRightDrawable = style.customDrawable("group_box_frame_up_right");
94         if (!upLeftDrawable.isNull)
95             _topFrameHeight = upLeftDrawable.height;
96         if (!upRightDrawable.isNull && _topFrameHeight < upRightDrawable.height)
97             _topFrameHeight = upRightDrawable.height;
98         _topFrameLeft = 0;
99         if (!upLeftDrawable.isNull)
100             _topFrameLeft = upLeftDrawable.width;
101         _topFrameRight = 0;
102         if (!upRightDrawable.isNull)
103             _topFrameRight = upRightDrawable.width;
104         _frameLeft = _frameRight = _frameBottom = _frameWidth = 0;
105         DrawableRef bottomDrawable = style.customDrawable("group_box_frame_bottom");
106         if (!bottomDrawable.isNull) {
107             Rect dp = bottomDrawable.padding;
108             _frameLeft = dp.left;
109             _frameRight = dp.right;
110             _frameBottom = dp.bottom;
111             _frameWidth = bottomDrawable.width;
112             _frameHeight = bottomDrawable.height;
113         }
114         _topHeight = _captionHeight;
115         if (_topHeight < _topFrameHeight)
116             _topHeight = _topFrameHeight;
117     }
118 
119     /// handle theme change: e.g. reload some themed resources
120     override void onThemeChanged() {
121         super.onThemeChanged();
122         _caption.onThemeChanged();
123     }
124 
125     /// get padding (between background bounds and content of widget)
126     override @property Rect padding() const {
127         // get default padding
128         Rect p = super.padding();
129         // correct padding based on frame drawables and caption
130         (cast(GroupBox)this).calcFrame(); // hack
131         if (p.top < _topHeight)
132             p.top = _topHeight;
133         if (p.left < _frameLeft)
134             p.left = _frameLeft;
135         if (p.right < _frameRight)
136             p.right = _frameRight;
137         if (p.bottom < _frameBottom)
138             p.bottom = _frameBottom;
139         return p;
140     }
141 
142     /// helper function for implement measure() when widget's content dimensions are known
143     override protected void measuredContent(int parentWidth, int parentHeight, int contentWidth, int contentHeight) {
144         _caption.measure(parentWidth, parentHeight);
145         calcFrame();
146         int topPadding = _topFrameLeft + _topFrameRight;
147         int bottomPadding = _frameLeft + _frameRight;
148         int extraTop = topPadding - bottomPadding;
149         int w = _caption.measuredWidth + extraTop;
150         if (contentWidth < w)
151             contentWidth = w;
152         super.measuredContent(parentWidth, parentHeight, contentWidth, contentHeight);
153     }
154 
155     /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
156     override void layout(Rect rc) {
157         super.layout(rc);
158         Rect r = rc;
159         r.top += margins.top;
160         r.bottom = r.top + _topHeight;
161         r.left += _topFrameLeft + margins.left;
162         r.right -= _topFrameRight;
163         _caption.measure(r.width, r.height);
164         if (r.width > _caption.measuredWidth)
165             r.right = r.left + _caption.measuredWidth;
166         _caption.layout(r);
167     }
168 
169     /// set padding for widget - override one from style
170     override @property Widget padding(Rect rc) {
171         return super.padding(rc);
172     }
173 
174     /// Draw widget at its position to buffer
175     override void onDraw(DrawBuf buf) {
176         if (visibility != Visibility.Visible)
177             return;
178         super.onDraw(buf);
179         Rect rc = _pos;
180         applyMargins(rc);
181 
182         _caption.onDraw(buf);
183 
184         int dh = 0;
185         if (_topFrameHeight < _captionHeight)
186             dh = (_captionHeight - _topFrameHeight) / 2;
187 
188         DrawableRef upLeftDrawable = style.customDrawable("group_box_frame_up_left");
189         if (!upLeftDrawable.isNull) {
190             upLeftDrawable.drawTo(buf, Rect(rc.left, rc.top + dh, rc.left + _topFrameLeft, rc.top + _topHeight));
191         }
192         DrawableRef upRightDrawable = style.customDrawable("group_box_frame_up_right");
193         if (!upRightDrawable.isNull) {
194             int cw = _caption.width;
195             upRightDrawable.drawTo(buf, Rect(rc.left + _topFrameLeft + cw, rc.top + dh, rc.right, rc.top + _topHeight));
196         }
197 
198         DrawableRef bottomDrawable = style.customDrawable("group_box_frame_bottom");
199         if (!bottomDrawable.isNull) {
200             bottomDrawable.drawTo(buf, Rect(rc.left, rc.top + _topHeight, rc.right, rc.bottom));
201         }
202     }
203 }