From 36eda44f3deb8b431f55ee74d1e71fe0a2bbe31d Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Mon, 10 Oct 2016 12:36:09 +0300 Subject: [PATCH] GroupBox widget implemented --- dlangui-msvc.visualdproj | 1 + examples/example1/src/example1.d | 21 +++ src/dlangui/package.d | 1 + src/dlangui/platforms/common/startup.d | 2 + src/dlangui/widgets/groupbox.d | 207 +++++++++++++++++++++++ src/dlangui/widgets/layouts.d | 4 +- src/dlangui/widgets/styles.d | 6 +- src/dlangui/widgets/widget.d | 4 +- views/res/group_box_frame_bottom.9.png | Bin 0 -> 319 bytes views/res/group_box_frame_up_left.9.png | Bin 0 -> 243 bytes views/res/group_box_frame_up_right.9.png | Bin 0 -> 257 bytes views/res/theme_default.xml | 13 ++ views/standard_resources.list | 3 + 13 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 src/dlangui/widgets/groupbox.d create mode 100644 views/res/group_box_frame_bottom.9.png create mode 100644 views/res/group_box_frame_up_left.9.png create mode 100644 views/res/group_box_frame_up_right.9.png diff --git a/dlangui-msvc.visualdproj b/dlangui-msvc.visualdproj index 05ebb8a0..91d25095 100644 --- a/dlangui-msvc.visualdproj +++ b/dlangui-msvc.visualdproj @@ -976,6 +976,7 @@ + diff --git a/examples/example1/src/example1.d b/examples/example1/src/example1.d index 8f1a2a8d..8e2b39cb 100644 --- a/examples/example1/src/example1.d +++ b/examples/example1/src/example1.d @@ -417,6 +417,27 @@ extern (C) int UIAppMain(string[] args) { }; tabs.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); + // most of controls example + { + LinearLayout controls = new VerticalLayout("controls"); + controls.padding = Rect(5,5,5,5); + HorizontalLayout line1 = new HorizontalLayout(); + controls.addChild(line1); + GroupBox gb = new GroupBox("checkboxes", "Check boxes"d); + gb.addChild(new CheckBox("cb1", "Check Box 1"d)); + gb.addChild(new CheckBox("cb2", "Check Box 2"d).checked(true)); + gb.addChild(new CheckBox("cb3", "Check Box 3 (disabled)"d).enabled(false)); + line1.addChild(gb); + + GroupBox gb2 = new GroupBox("radiobuttons", "Radio buttons"d); + gb2.addChild(new RadioButton("rb1", "Radio button 1"d).checked(true)); + gb2.addChild(new RadioButton("rb2", "Radio button 2"d)); + gb2.addChild(new RadioButton("rb3", "Radio button (disabled)"d).enabled(false)); + line1.addChild(gb2); + + tabs.addTab(controls, "Controls"d); + } + LinearLayout layout = new LinearLayout("tab1"); layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0")); diff --git a/src/dlangui/package.d b/src/dlangui/package.d index 0c3c11ac..c675cd49 100644 --- a/src/dlangui/package.d +++ b/src/dlangui/package.d @@ -60,6 +60,7 @@ public { import dlangui.widgets.widget; import dlangui.widgets.controls; import dlangui.widgets.layouts; + import dlangui.widgets.groupbox; import dlangui.widgets.lists; import dlangui.widgets.tabs; import dlangui.widgets.menu; diff --git a/src/dlangui/platforms/common/startup.d b/src/dlangui/platforms/common/startup.d index 8e3b571e..e8881f1d 100644 --- a/src/dlangui/platforms/common/startup.d +++ b/src/dlangui/platforms/common/startup.d @@ -325,6 +325,7 @@ void registerStandardWidgets() { import dlangui.widgets.combobox; import dlangui.widgets.editors; import dlangui.widgets.grid; + import dlangui.widgets.groupbox; import dlangui.dialogs.filedlg; import dlangui.widgets.menu; mixin(registerWidgets!(FileNameEditLine, DirEditLine, //dlangui.dialogs.filedlg @@ -332,6 +333,7 @@ void registerStandardWidgets() { Widget, TextWidget, MultilineTextWidget, Button, ImageWidget, ImageButton, ImageCheckButton, ImageTextButton, SwitchButton, RadioButton, CheckBox, ScrollBar, SliderWidget, HSpacer, VSpacer, CanvasWidget, // dlangui.widgets.controls EditLine, EditBox, LogWidget,//dlangui.widgets.editors + GroupBox, // dlangui.widgets.groupbox StringGridWidget, //dlangui.widgets.grid VerticalLayout, HorizontalLayout, TableLayout, FrameLayout, // dlangui.widgets.layouts ListWidget, StringListWidget,//dlangui.widgets.lists diff --git a/src/dlangui/widgets/groupbox.d b/src/dlangui/widgets/groupbox.d new file mode 100644 index 00000000..1fa0715a --- /dev/null +++ b/src/dlangui/widgets/groupbox.d @@ -0,0 +1,207 @@ +// Written in the D programming language. + +/** +This module contains Group Box widget implementation. + +Group box is linear layout with frame and caption for grouping controls. + + +Synopsis: + +---- +import dlangui.widgets.groupbox; + +---- + +Copyright: Vadim Lopatin, 2016 +License: Boost License 1.0 +Authors: Vadim Lopatin, coolreader.org@gmail.com +*/ +module dlangui.widgets.groupbox; + +import dlangui.widgets.widget; +import dlangui.widgets.layouts; + +class GroupBox : LinearLayout { + import dlangui.widgets.controls; + + protected TextWidget _caption; + + this() { + super("GROUP_BOX", Orientation.Vertical); + styleId = STYLE_GROUP_BOX; + _caption = new TextWidget("GROUP_BOX_CAPTION"); + _caption.styleId = STYLE_GROUP_BOX_CAPTION; + + } + + this(string ID, UIString uitext, Orientation orientation = Orientation.Vertical) { + super(ID, orientation); + styleId = STYLE_GROUP_BOX; + _caption = new TextWidget("GROUP_BOX_CAPTION"); + _caption.styleId = STYLE_GROUP_BOX_CAPTION; + _caption.parent = this; + text = uitext; + } + + this(string ID, string textResourceId, Orientation orientation = Orientation.Vertical) { + super(ID, orientation); + styleId = STYLE_GROUP_BOX; + _caption = new TextWidget("GROUP_BOX_CAPTION"); + _caption.styleId = STYLE_GROUP_BOX_CAPTION; + _caption.parent = this; + textResource = textResourceId; + } + + this(string ID, dstring rawText, Orientation orientation = Orientation.Vertical) { + super(ID, orientation); + _caption = new TextWidget("GROUP_BOX_CAPTION"); + _caption.styleId = STYLE_GROUP_BOX_CAPTION; + _caption.parent = this; + styleId = STYLE_GROUP_BOX; + text = rawText; + } + + ~this() { + destroy(_caption); + } + + /// get widget text + override @property dstring text() { return _caption.text; } + /// set text to show + override @property Widget text(dstring s) { + _caption.text = s; + requestLayout(); + return this; + } + /// set text to show + override @property Widget text(UIString s) { + _caption.text = s; + requestLayout(); + return this; + } + /// set text resource ID to show + @property Widget textResource(string s) { + _caption.textResource = s; + requestLayout(); + return this; + } + + int _topFrameHeight; + int _topFrameLeft; + int _topFrameRight; + int _captionHeight; + int _topHeight; + int _frameLeft; + int _frameRight; + int _frameBottom; + int _frameWidth; + int _frameHeight; + protected void calcFrame() { + Rect captPadding = _caption.padding; + Rect captMargins = _caption.margins; + int captFontHeight = _caption.font.height; + _captionHeight = captPadding.top + captPadding.bottom + captMargins.top + captMargins.bottom + captFontHeight; + _topFrameHeight = 0; + DrawableRef upLeftDrawable = style.customDrawable("group_box_frame_up_left"); + DrawableRef upRightDrawable = style.customDrawable("group_box_frame_up_right"); + if (!upLeftDrawable.isNull) + _topFrameHeight = upLeftDrawable.height; + if (!upRightDrawable.isNull && _topFrameHeight < upRightDrawable.height) + _topFrameHeight = upRightDrawable.height; + _topFrameLeft = 0; + if (!upLeftDrawable.isNull) + _topFrameLeft = upLeftDrawable.width; + _topFrameRight = 0; + if (!upRightDrawable.isNull) + _topFrameRight = upRightDrawable.width; + _frameLeft = _frameRight = _frameBottom = _frameWidth = 0; + DrawableRef bottomDrawable = style.customDrawable("group_box_frame_bottom"); + if (!bottomDrawable.isNull) { + Rect dp = bottomDrawable.padding; + _frameLeft = dp.left; + _frameRight = dp.right; + _frameBottom = dp.bottom; + _frameWidth = bottomDrawable.width; + _frameHeight = bottomDrawable.height; + } + _topHeight = _captionHeight; + if (_topHeight < _topFrameHeight) + _topHeight = _topFrameHeight; + } + + /// get padding (between background bounds and content of widget) + override @property Rect padding() const { + // get default padding + Rect p = super.padding(); + // correct padding based on frame drawables and caption + (cast(GroupBox)this).calcFrame(); // hack + if (p.top < _topHeight) + p.top = _topHeight; + if (p.left < _frameLeft) + p.left = _frameLeft; + if (p.right < _frameRight) + p.right = _frameRight; + if (p.bottom < _frameBottom) + p.bottom = _frameBottom; + return p; + } + + /// helper function for implement measure() when widget's content dimensions are known + override protected void measuredContent(int parentWidth, int parentHeight, int contentWidth, int contentHeight) { + _caption.measure(parentWidth, parentHeight); + calcFrame(); + int w = _caption.measuredWidth; + if (contentWidth < w) + contentWidth = w; + super.measuredContent(parentWidth, parentHeight, contentWidth, contentHeight); + } + + /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). + override void layout(Rect rc) { + super.layout(rc); + Rect r = rc; + r.bottom = r.top + _topHeight; + r.left += _topFrameLeft; + r.right -= _topFrameRight; + _caption.measure(r.width, r.height); + if (r.width > _caption.measuredWidth) + r.right = r.left + _caption.measuredWidth; + _caption.layout(r); + } + + /// set padding for widget - override one from style + override @property Widget padding(Rect rc) { + return super.padding(rc); + } + + /// Draw widget at its position to buffer + override void onDraw(DrawBuf buf) { + if (visibility != Visibility.Visible) + return; + super.onDraw(buf); + Rect rc = _pos; + applyMargins(rc); + + _caption.onDraw(buf); + + int dh = 0; + if (_topFrameHeight < _captionHeight) + dh = (_captionHeight - _topFrameHeight) / 2; + + DrawableRef upLeftDrawable = style.customDrawable("group_box_frame_up_left"); + if (!upLeftDrawable.isNull) { + upLeftDrawable.drawTo(buf, Rect(rc.left, rc.top + dh, rc.left + _topFrameLeft, rc.top + _topHeight)); + } + DrawableRef upRightDrawable = style.customDrawable("group_box_frame_up_right"); + if (!upRightDrawable.isNull) { + int cw = _caption.width; + upRightDrawable.drawTo(buf, Rect(rc.left + _topFrameLeft + cw, rc.top + dh, rc.right, rc.top + _topHeight)); + } + + DrawableRef bottomDrawable = style.customDrawable("group_box_frame_bottom"); + if (!bottomDrawable.isNull) { + bottomDrawable.drawTo(buf, Rect(rc.left, rc.top + _topHeight, rc.right, rc.bottom)); + } + } +} diff --git a/src/dlangui/widgets/layouts.d b/src/dlangui/widgets/layouts.d index a6c9dccb..532f0134 100644 --- a/src/dlangui/widgets/layouts.d +++ b/src/dlangui/widgets/layouts.d @@ -510,8 +510,8 @@ class LinearLayout : WidgetGroupDefaultDrawing { this() { this(null); } - /// create with ID parameter - this(string ID) { + /// create with ID parameter and orientation + this(string ID, Orientation orientation = Orientation.Vertical) { super(ID); _layoutItems = new LayoutItems(); } diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index 8e5d5589..53b3012e 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -115,6 +115,10 @@ immutable string STYLE_EDIT_BOX = "EDIT_BOX"; immutable string STYLE_STRING_GRID = "STRING_GRID"; /// standard style id for background similar to transparent button immutable string STYLE_TRANSPARENT_BUTTON_BACKGROUND = "TRANSPARENT_BUTTON_BACKGROUND"; +/// standard style id for GroupBox +immutable string STYLE_GROUP_BOX = "GROUP_BOX"; +/// standard style id for GroupBox caption +immutable string STYLE_GROUP_BOX_CAPTION = "GROUP_BOX_CAPTION"; /// standard style id for tree item immutable string STYLE_TREE_ITEM = "TREE_ITEM"; @@ -364,7 +368,7 @@ public: } /// get custom drawable attribute - ref DrawableRef customDrawable(string id) { + ref DrawableRef customDrawable(string id) const { if (id in _customDrawables) return _customDrawables[id].drawable; return parentStyle ? parentStyle.customDrawable(id) : currentTheme.customDrawable(id); diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 02e8370c..75ed2ceb 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -407,14 +407,14 @@ public: @property Widget backgroundColor(uint color) { ownStyle.backgroundColor = color; invalidate(); - return this; + return this; } /// set background color for widget - from string like "#5599CC" or "white" @property Widget backgroundColor(string colorString) { uint color = decodeHexColor(colorString, COLOR_TRANSPARENT); ownStyle.backgroundColor = color; invalidate(); - return this; + return this; } /// background image id diff --git a/views/res/group_box_frame_bottom.9.png b/views/res/group_box_frame_bottom.9.png new file mode 100644 index 0000000000000000000000000000000000000000..3d3a61cd88c44ce72c9ceaf8d3323422ef135402 GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^PCzWh!3HFmJG+X26kC$Fy9>jA5L~c#`DCC7XMsm# zF#`j)FbFd;%$g$s6l5>)^mS!_z{SNWqZ(`f8>CY#}6Z6r=jfIW<4+?whu$Uy#w&*Hh+S3G#=htDnm{r-UW| DO{;7l literal 0 HcmV?d00001 diff --git a/views/res/group_box_frame_up_left.9.png b/views/res/group_box_frame_up_left.9.png new file mode 100644 index 0000000000000000000000000000000000000000..53844a0d940175e021605258a8bae758a45e21c7 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^5|gW!U_%O?XxI14-? ziy0WWg+Z8+Vb&aw9`+JXUsv`ATwI(o#w%a@DFcOMGeaUuobz*YQ}arITm}Z`qSVBa z)D(sC%#sWRcTeAd6une-pm?~ai(`nz>Esjzxj*iYjg5Mm0u5{!?rqtVb#>MM7M>kt zZ>8dlBRC`lq@=ti@<()hQd3sGxpNw)x1dAl0hh-X|NOJLCERo+WW+9PN=r&n+$f=7 bdftGEVH+>6YG6l#JIHQNS3j3^P6|gW!U_%O?XxI14-? ziy0WWg+Z8+Vb&aw9`+JXUsv`ATwI*8j1}s7kAOn5nIRD+&iT2ysd*(pE(3#eQEFmI zYKlU6W=V#EyQgnJie4%^P(0Ps#WBR<^xLTiIS&}{FfYB3X*A*aNgGLx*DL-{G2=TL z(Dgo@XVL)%=#--mPMrg}#M9N!Wt~$(699HZS5yE1 literal 0 HcmV?d00001 diff --git a/views/res/theme_default.xml b/views/res/theme_default.xml index 2d5cd4f3..e9787ab2 100644 --- a/views/res/theme_default.xml +++ b/views/res/theme_default.xml @@ -196,6 +196,7 @@ + + + + diff --git a/views/standard_resources.list b/views/standard_resources.list index c15804eb..0e26052f 100644 --- a/views/standard_resources.list +++ b/views/standard_resources.list @@ -47,6 +47,9 @@ res/dock_window_caption_background_normal_down_dark.9.png res/fileclose.png res/fileopen.png res/frame_blue.9.png +res/group_box_frame_bottom.9.png +res/group_box_frame_up_left.9.png +res/group_box_frame_up_right.9.png res/i18n/std_en.ini res/i18n/std_ru.ini res/list_item_background.xml