tabs
|
@ -50,14 +50,12 @@ extern (C) int UIAppMain(string[] args) {
|
||||||
LinearLayout layout = new LinearLayout();
|
LinearLayout layout = new LinearLayout();
|
||||||
|
|
||||||
TabWidget tabs = new TabWidget("TABS");
|
TabWidget tabs = new TabWidget("TABS");
|
||||||
tabs.addTab("id1", "Tab 1"d);
|
tabs.addTab((new TextWidget()).id("tab1").textColor(0x00802000).text("Tab 1 contents"), "Tab 1"d);
|
||||||
tabs.addTab("id2", "Tab 2 label"d);
|
tabs.addTab((new TextWidget()).id("tab2").textColor(0x00802000).text("Tab 2 contents"), "Tab 2"d);
|
||||||
tabs.addTab("id3", "Tab 3 label"d);
|
tabs.addTab((new TextWidget()).id("tab3").textColor(0x00802000).text("Tab 3 contents"), "Tab 3"d);
|
||||||
tabs.addTab("id4", "Tab 4 label"d);
|
tabs.addTab((new TextWidget()).id("tab4").textColor(0x00802000).text("Tab 4 contents"), "Tab 4"d);
|
||||||
tabs.addTab("id5", "Tab 5 label"d);
|
tabs.addTab((new TextWidget()).id("tab5").textColor(0x00802000).text("Tab 5 contents"), "Tab 5"d);
|
||||||
tabs.addTab("id6", "Tab 6 label"d);
|
tabs.selectTab("tab1");
|
||||||
tabs.addTab("id7", "Tab 7 label"d);
|
|
||||||
tabs.addTab("id8", "Tab 8 label"d);
|
|
||||||
layout.addChild(tabs);
|
layout.addChild(tabs);
|
||||||
|
|
||||||
layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0"));
|
layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0"));
|
||||||
|
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 972 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 256 B |
After Width: | Height: | Size: 258 B |
After Width: | Height: | Size: 258 B |
|
@ -254,6 +254,7 @@ class LinearLayout : WidgetGroup {
|
||||||
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
|
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
|
||||||
override void layout(Rect rc) {
|
override void layout(Rect rc) {
|
||||||
if (visibility == Visibility.Gone) {
|
if (visibility == Visibility.Gone) {
|
||||||
|
_needLayout = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_pos = rc;
|
_pos = rc;
|
||||||
|
@ -295,3 +296,83 @@ class HorizontalLayout : LinearLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// place all children into same place (usually, only one child should be visible at a time)
|
||||||
|
class FrameLayout : WidgetGroup {
|
||||||
|
this(string ID) {
|
||||||
|
super(ID);
|
||||||
|
}
|
||||||
|
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
|
||||||
|
override void measure(int parentWidth, int parentHeight) {
|
||||||
|
Rect m = margins;
|
||||||
|
Rect p = padding;
|
||||||
|
// calc size constraints for children
|
||||||
|
int pwidth = parentWidth;
|
||||||
|
int pheight = parentHeight;
|
||||||
|
if (parentWidth != SIZE_UNSPECIFIED)
|
||||||
|
pwidth -= m.left + m.right + p.left + p.right;
|
||||||
|
if (parentHeight != SIZE_UNSPECIFIED)
|
||||||
|
pheight -= m.top + m.bottom + p.top + p.bottom;
|
||||||
|
// measure children
|
||||||
|
Point sz;
|
||||||
|
for (int i = 0; i < _children.count; i++) {
|
||||||
|
Widget item = _children.get(i);
|
||||||
|
if (item.visibility != Visibility.Gone) {
|
||||||
|
item.measure(pwidth, pheight);
|
||||||
|
if (sz.x < item.measuredWidth)
|
||||||
|
sz.x = item.measuredWidth;
|
||||||
|
if (sz.y < item.measuredHeight)
|
||||||
|
sz.y = item.measuredHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
|
||||||
|
override void layout(Rect rc) {
|
||||||
|
if (visibility == Visibility.Gone) {
|
||||||
|
_needLayout = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pos = rc;
|
||||||
|
applyMargins(rc);
|
||||||
|
applyPadding(rc);
|
||||||
|
for (int i = 0; i < _children.count; i++) {
|
||||||
|
Widget item = _children.get(i);
|
||||||
|
if (item.visibility != Visibility.Gone) {
|
||||||
|
item.layout(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);
|
||||||
|
applyPadding(rc);
|
||||||
|
ClipRectSaver(buf, rc);
|
||||||
|
for (int i = 0; i < _children.count; i++) {
|
||||||
|
Widget item = _children.get(i);
|
||||||
|
if (item.visibility != Visibility.Visible)
|
||||||
|
continue;
|
||||||
|
item.onDraw(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// make one of children (with specified ID) visible, for the rest, set visibility to otherChildrenVisibility
|
||||||
|
bool showChild(string ID, Visibility otherChildrenVisibility = Visibility.Invisible) {
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < _children.count; i++) {
|
||||||
|
Widget item = _children.get(i);
|
||||||
|
if (item.compareId(ID)) {
|
||||||
|
item.visibility = Visibility.Visible;
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
item.visibility = otherChildrenVisibility;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ enum State : uint {
|
||||||
Focused = 2,
|
Focused = 2,
|
||||||
Disabled = 4,
|
Disabled = 4,
|
||||||
Hover = 8, // mouse pointer is over control, buttons not pressed
|
Hover = 8, // mouse pointer is over control, buttons not pressed
|
||||||
|
Selected = 16,
|
||||||
|
Parent = 128, // use parent's state
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Align : ubyte {
|
enum Align : ubyte {
|
||||||
|
@ -616,6 +618,7 @@ immutable ATTR_SCROLLBAR_INDICATOR_HORIZONTAL = "scrollbar_indicator_horizontal"
|
||||||
Theme createDefaultTheme() {
|
Theme createDefaultTheme() {
|
||||||
Log.d("Creating default theme");
|
Log.d("Creating default theme");
|
||||||
Theme res = new Theme("default");
|
Theme res = new Theme("default");
|
||||||
|
res.fontSize(14);
|
||||||
Style button = res.createSubstyle("BUTTON").backgroundImageId("btn_default_small_normal").alignment(Align.Center);
|
Style button = res.createSubstyle("BUTTON").backgroundImageId("btn_default_small_normal").alignment(Align.Center);
|
||||||
Style text = res.createSubstyle("TEXT").margins(Rect(2,2,2,2)).padding(Rect(1,1,1,1));
|
Style text = res.createSubstyle("TEXT").margins(Rect(2,2,2,2)).padding(Rect(1,1,1,1));
|
||||||
button.createState(State.Disabled | State.Focused, State.Disabled | State.Focused).backgroundImageId("btn_default_small_normal_disable_focused");
|
button.createState(State.Disabled | State.Focused, State.Disabled | State.Focused).backgroundImageId("btn_default_small_normal_disable_focused");
|
||||||
|
@ -638,6 +641,24 @@ Theme createDefaultTheme() {
|
||||||
scrollbarPage.createState(State.Pressed, State.Pressed).backgroundColor(0xC0404080);
|
scrollbarPage.createState(State.Pressed, State.Pressed).backgroundColor(0xC0404080);
|
||||||
scrollbarPage.createState(State.Hover, State.Hover).backgroundColor(0xF0404080);
|
scrollbarPage.createState(State.Hover, State.Hover).backgroundColor(0xF0404080);
|
||||||
|
|
||||||
|
Style tabUp = res.createSubstyle("TAB_UP");
|
||||||
|
tabUp.backgroundImageId("tab_up_background");
|
||||||
|
tabUp.layoutWidth(FILL_PARENT);
|
||||||
|
tabUp.createState(State.Selected, State.Selected).backgroundImageId("tab_up_backgrond_selected");
|
||||||
|
Style tabUpButtonText = res.createSubstyle("TAB_UP_BUTTON_TEXT");
|
||||||
|
tabUpButtonText.textColor(0xFFFFFF).fontSize(12);
|
||||||
|
tabUpButtonText.createState(State.Selected, State.Selected).textColor(0x000000);
|
||||||
|
tabUpButtonText.createState(State.Selected|State.Focused, State.Selected|State.Focused).textColor(0x000000);
|
||||||
|
tabUpButtonText.createState(State.Focused, State.Focused).textColor(0x000000);
|
||||||
|
tabUpButtonText.createState(State.Hover, State.Hover).textColor(0x404000);
|
||||||
|
Style tabUpButton = res.createSubstyle("TAB_UP_BUTTON");
|
||||||
|
tabUpButton.backgroundImageId("tab_btn_up_normal");
|
||||||
|
tabUpButton.createState(State.Selected, State.Selected).backgroundImageId("tab_btn_up_selected");
|
||||||
|
tabUpButton.createState(State.Selected|State.Focused, State.Selected|State.Focused).backgroundImageId("tab_btn_up_focused_selected");
|
||||||
|
tabUpButton.createState(State.Focused, State.Focused).backgroundImageId("tab_btn_up_focused");
|
||||||
|
tabUpButton.createState(State.Hover, State.Hover).backgroundImageId("tab_btn_up_hover");
|
||||||
|
Style tabHost = res.createSubstyle("TAB_HOST");
|
||||||
|
tabHost.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
||||||
//res.dumpStats();
|
//res.dumpStats();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,402 @@
|
||||||
|
module dlangui.widgets.tabs;
|
||||||
|
|
||||||
|
import dlangui.widgets.layouts;
|
||||||
|
import dlangui.widgets.controls;
|
||||||
|
|
||||||
|
class TabItem {
|
||||||
|
private string _iconRes;
|
||||||
|
private string _id;
|
||||||
|
private UIString _label;
|
||||||
|
private long _lastAccessTs;
|
||||||
|
this(string id, string labelRes, string iconRes = null) {
|
||||||
|
_id = id;
|
||||||
|
_label = labelRes;
|
||||||
|
_iconRes = iconRes;
|
||||||
|
}
|
||||||
|
this(string id, dstring labelRes, string iconRes = null) {
|
||||||
|
_id = id;
|
||||||
|
_label = labelRes;
|
||||||
|
_iconRes = iconRes;
|
||||||
|
_lastAccessTs = std.datetime.Clock.currStdTime;
|
||||||
|
}
|
||||||
|
@property string iconId() const { return _iconRes; }
|
||||||
|
@property string id() const { return _id; }
|
||||||
|
@property ref UIString text() { return _label; }
|
||||||
|
@property TabItem iconId(string id) { _iconRes = id; return this; }
|
||||||
|
@property TabItem id(string id) { _id = id; return this; }
|
||||||
|
@property long lastAccessTs() { return _lastAccessTs; }
|
||||||
|
@property void lastAccessTs(long ts) { _lastAccessTs = ts; }
|
||||||
|
void updateAccessTs() { _lastAccessTs = std.datetime.Clock.currStdTime; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class TabItemWidget : HorizontalLayout {
|
||||||
|
private ImageWidget _icon;
|
||||||
|
private TextWidget _label;
|
||||||
|
private ImageButton _closeButton;
|
||||||
|
private TabItem _item;
|
||||||
|
private bool _enableCloseButton;
|
||||||
|
@property TabItem tabItem() { return _item; }
|
||||||
|
@property TabControl tabControl() { return cast(TabControl)parent; }
|
||||||
|
this(TabItem item, bool enableCloseButton = true) {
|
||||||
|
styleId = "TAB_UP_BUTTON";
|
||||||
|
_enableCloseButton = enableCloseButton;
|
||||||
|
_icon = new ImageWidget();
|
||||||
|
_label = new TextWidget();
|
||||||
|
_label.styleId = "TAB_UP_BUTTON_TEXT";
|
||||||
|
_label.state = State.Parent;
|
||||||
|
_closeButton = new ImageButton("CLOSE");
|
||||||
|
_closeButton.drawableId = "close";
|
||||||
|
_closeButton.onClickListener = &onClick;
|
||||||
|
if (_enableCloseButton) {
|
||||||
|
_closeButton.visibility = Visibility.Gone;
|
||||||
|
} else {
|
||||||
|
_closeButton.visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
addChild(_icon);
|
||||||
|
addChild(_label);
|
||||||
|
addChild(_closeButton);
|
||||||
|
setItem(item);
|
||||||
|
trackHover = true;
|
||||||
|
}
|
||||||
|
protected bool onClick(Widget source) {
|
||||||
|
if (source.compareId("CLOSE")) {
|
||||||
|
Log.d("tab close button pressed");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
protected void setItem(TabItem item) {
|
||||||
|
_item = item;
|
||||||
|
if (item.iconId !is null) {
|
||||||
|
_icon.visibility = Visibility.Visible;
|
||||||
|
_icon.drawableId = item.iconId;
|
||||||
|
} else {
|
||||||
|
_icon.visibility = Visibility.Gone;
|
||||||
|
}
|
||||||
|
_label.text = item.text;
|
||||||
|
id = item.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// tab item list helper class
|
||||||
|
class TabItemList {
|
||||||
|
private TabItem[] _list;
|
||||||
|
private int _len;
|
||||||
|
|
||||||
|
this() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get item by index
|
||||||
|
TabItem get(int index) {
|
||||||
|
if (index < 0 || index >= _len)
|
||||||
|
return null;
|
||||||
|
return _list[index];
|
||||||
|
}
|
||||||
|
/// get item by id
|
||||||
|
TabItem get(string id) {
|
||||||
|
int idx = indexById(id);
|
||||||
|
if (idx < 0)
|
||||||
|
return null;
|
||||||
|
return _list[idx];
|
||||||
|
}
|
||||||
|
@property int length() const { return _len; }
|
||||||
|
/// append new item
|
||||||
|
TabItemList add(TabItem item) {
|
||||||
|
return insert(item, -1);
|
||||||
|
}
|
||||||
|
/// insert new item to specified position
|
||||||
|
TabItemList insert(TabItem item, int index) {
|
||||||
|
if (index > _len || index < 0)
|
||||||
|
index = _len;
|
||||||
|
if (_list.length <= _len)
|
||||||
|
_list.length = _len + 4;
|
||||||
|
for (int i = _len; i > index; i--)
|
||||||
|
_list[i] = _list[i - 1];
|
||||||
|
_list[index] = item;
|
||||||
|
_len++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// remove item by index
|
||||||
|
TabItem remove(int index) {
|
||||||
|
TabItem res = _list[index];
|
||||||
|
for (int i = index; i < _len - 1; i++)
|
||||||
|
_list[i] = _list[i + 1];
|
||||||
|
_len--;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/// find tab index by id
|
||||||
|
int indexById(string id) {
|
||||||
|
import std.algorithm;
|
||||||
|
for (int i = 0; i < _len; i++) {
|
||||||
|
if (_list[i].id.equal(id))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TabControl : WidgetGroup {
|
||||||
|
protected TabItemList _items;
|
||||||
|
protected ImageButton _moreButton;
|
||||||
|
protected bool _enableCloseButton;
|
||||||
|
protected TabItemWidget[] _sortedItems;
|
||||||
|
|
||||||
|
this(string ID) {
|
||||||
|
super(ID);
|
||||||
|
_items = new TabItemList();
|
||||||
|
_moreButton = new ImageButton("MORE", "tab_more");
|
||||||
|
_moreButton.onClickListener = &onClick;
|
||||||
|
_enableCloseButton = true;
|
||||||
|
styleId = "TAB_UP";
|
||||||
|
addChild(_moreButton); // first child is always MORE button, the rest corresponds to tab list
|
||||||
|
}
|
||||||
|
/// returns tab count
|
||||||
|
@property int tabCount() const {
|
||||||
|
return _items.length;
|
||||||
|
}
|
||||||
|
/// returns tab item by id (null if index out of range)
|
||||||
|
TabItem tab(int index) {
|
||||||
|
return _items.get(index);
|
||||||
|
}
|
||||||
|
/// returns tab item by id (null if not found)
|
||||||
|
TabItem tab(string id) {
|
||||||
|
return _items.get(id);
|
||||||
|
}
|
||||||
|
/// get tab index by tab id (-1 if not found)
|
||||||
|
int tabIndex(string id) {
|
||||||
|
return _items.indexById(id);
|
||||||
|
}
|
||||||
|
protected void updateTabs() {
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
static bool accessTimeComparator(TabItemWidget a, TabItemWidget b) {
|
||||||
|
return (a.tabItem.lastAccessTs > b.tabItem.lastAccessTs);
|
||||||
|
}
|
||||||
|
protected TabItemWidget[] sortedItems() {
|
||||||
|
_sortedItems.length = _items.length;
|
||||||
|
for (int i = 0; i < _items.length; i++)
|
||||||
|
_sortedItems[i] = cast(TabItemWidget)_children.get(i + 1);
|
||||||
|
std.algorithm.sort!(accessTimeComparator)(_sortedItems);
|
||||||
|
return _sortedItems;
|
||||||
|
}
|
||||||
|
/// remove tab
|
||||||
|
TabControl removeTab(string id) {
|
||||||
|
int index = _items.indexById(id);
|
||||||
|
if (index >= 0) {
|
||||||
|
_children.remove(index + 1);
|
||||||
|
_items.remove(index);
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// add new tab
|
||||||
|
TabControl addTab(TabItem item, int index = -1, bool enableCloseButton = false) {
|
||||||
|
_items.insert(item, index);
|
||||||
|
TabItemWidget widget = new TabItemWidget(item, enableCloseButton);
|
||||||
|
widget.parent = this;
|
||||||
|
widget.onClickListener = &onClick;
|
||||||
|
_children.insert(widget, index);
|
||||||
|
updateTabs();
|
||||||
|
requestLayout();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// add new tab by id and label string
|
||||||
|
TabControl addTab(string id, dstring label, string iconId = null, bool enableCloseButton = false) {
|
||||||
|
TabItem item = new TabItem(id, label, iconId);
|
||||||
|
return addTab(item, -1, enableCloseButton);
|
||||||
|
}
|
||||||
|
/// add new tab by id and label string resource id
|
||||||
|
TabControl addTab(string id, string labelResourceId, string iconId = null, bool enableCloseButton = false) {
|
||||||
|
TabItem item = new TabItem(id, labelResourceId, iconId);
|
||||||
|
return addTab(item, -1, enableCloseButton);
|
||||||
|
}
|
||||||
|
protected bool onClick(Widget source) {
|
||||||
|
if (source.compareId("MORE")) {
|
||||||
|
Log.d("tab MORE button pressed");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
string id = source.id;
|
||||||
|
int index = tabIndex(id);
|
||||||
|
if (index >= 0) {
|
||||||
|
selectTab(index);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
|
||||||
|
override void measure(int parentWidth, int parentHeight) {
|
||||||
|
Rect m = margins;
|
||||||
|
Rect p = padding;
|
||||||
|
// calc size constraints for children
|
||||||
|
int pwidth = parentWidth;
|
||||||
|
int pheight = parentHeight;
|
||||||
|
if (parentWidth != SIZE_UNSPECIFIED)
|
||||||
|
pwidth -= m.left + m.right + p.left + p.right;
|
||||||
|
if (parentHeight != SIZE_UNSPECIFIED)
|
||||||
|
pheight -= m.top + m.bottom + p.top + p.bottom;
|
||||||
|
// measure children
|
||||||
|
Point sz;
|
||||||
|
_moreButton.measure(pwidth, pheight);
|
||||||
|
sz.x = _moreButton.measuredWidth;
|
||||||
|
sz.y = _moreButton.measuredHeight;
|
||||||
|
pwidth -= sz.x;
|
||||||
|
for (int i = 1; i < _children.count; i++) {
|
||||||
|
Widget tab = _children.get(i);
|
||||||
|
tab.visibility = Visibility.Visible;
|
||||||
|
tab.measure(pwidth, pheight);
|
||||||
|
if (sz.y < tab.measuredHeight)
|
||||||
|
sz.y = tab.measuredHeight;
|
||||||
|
if (sz.x + tab.measuredWidth > pwidth)
|
||||||
|
break;
|
||||||
|
sz.x += tab.measuredWidth;
|
||||||
|
}
|
||||||
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
||||||
|
}
|
||||||
|
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
|
||||||
|
override void layout(Rect rc) {
|
||||||
|
_needLayout = false;
|
||||||
|
if (visibility == Visibility.Gone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pos = rc;
|
||||||
|
applyMargins(rc);
|
||||||
|
applyPadding(rc);
|
||||||
|
// more button
|
||||||
|
Rect moreRc = rc;
|
||||||
|
moreRc.left = rc.right - _moreButton.measuredWidth;
|
||||||
|
_moreButton.layout(moreRc);
|
||||||
|
rc.right -= _moreButton.measuredWidth;
|
||||||
|
// tabs
|
||||||
|
int maxw = rc.width;
|
||||||
|
// measure and update visibility
|
||||||
|
TabItemWidget[] sorted = sortedItems();
|
||||||
|
int w = 0;
|
||||||
|
for (int i = 0; i < sorted.length; i++) {
|
||||||
|
TabItemWidget widget = sorted[i];
|
||||||
|
widget.visibility = Visibility.Visible;
|
||||||
|
widget.measure(rc.width, rc.height);
|
||||||
|
if (w + widget.measuredWidth < maxw) {
|
||||||
|
w += widget.measuredWidth;
|
||||||
|
} else {
|
||||||
|
widget.visibility = Visibility.Gone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// layout visible items
|
||||||
|
for (int i = 1; i < _children.count; i++) {
|
||||||
|
TabItemWidget widget = cast(TabItemWidget)_children.get(i);
|
||||||
|
if (widget.visibility != Visibility.Visible)
|
||||||
|
continue;
|
||||||
|
w = widget.measuredWidth;
|
||||||
|
rc.right = rc.left + w;
|
||||||
|
widget.layout(rc);
|
||||||
|
rc.left += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 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);
|
||||||
|
applyPadding(rc);
|
||||||
|
ClipRectSaver(buf, rc);
|
||||||
|
for (int i = 0; i < _children.count; i++) {
|
||||||
|
Widget item = _children.get(i);
|
||||||
|
if (item.visibility != Visibility.Visible)
|
||||||
|
continue;
|
||||||
|
item.onDraw(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectTab(int index) {
|
||||||
|
for (int i = 1; i < _children.count; i++) {
|
||||||
|
if (index == i - 1) {
|
||||||
|
_children.get(i).state = State.Selected;
|
||||||
|
} else {
|
||||||
|
_children.get(i).state = State.Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// container for widgets controlled by TabControl
|
||||||
|
class TabHost : FrameLayout {
|
||||||
|
this(string ID, TabControl tabControl) {
|
||||||
|
super(ID);
|
||||||
|
_tabControl = tabControl;
|
||||||
|
styleId = "TAB_HOST";
|
||||||
|
}
|
||||||
|
protected TabControl _tabControl;
|
||||||
|
/// get currently set control widget
|
||||||
|
@property TabControl tabControl() { return _tabControl; }
|
||||||
|
/// set new control widget
|
||||||
|
@property TabHost tabControl(TabControl newWidget) { _tabControl = newWidget; return this; }
|
||||||
|
|
||||||
|
/// remove tab
|
||||||
|
TabHost removeTab(string id) {
|
||||||
|
assert(_tabControl !is null, "No TabControl set for TabHost");
|
||||||
|
Widget child = removeChild(id);
|
||||||
|
if (child !is null) {
|
||||||
|
destroy(child);
|
||||||
|
}
|
||||||
|
_tabControl.removeTab(id);
|
||||||
|
requestLayout();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// add new tab by id and label string
|
||||||
|
TabHost addTab(Widget widget, dstring label, string iconId = null, bool enableCloseButton = false) {
|
||||||
|
assert(_tabControl !is null, "No TabControl set for TabHost");
|
||||||
|
assert(widget.id !is null, "ID for tab host page is mandatory");
|
||||||
|
assert(_children.indexOf(id) == -1, "duplicate ID for tab host page");
|
||||||
|
_tabControl.addTab(widget.id, label, iconId, enableCloseButton);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// add new tab by id and label string resource id
|
||||||
|
TabHost addTab(Widget widget, string labelResourceId, string iconId = null, bool enableCloseButton = false) {
|
||||||
|
assert(_tabControl !is null, "No TabControl set for TabHost");
|
||||||
|
assert(widget.id !is null, "ID for tab host page is mandatory");
|
||||||
|
assert(_children.indexOf(id) == -1, "duplicate ID for tab host page");
|
||||||
|
_tabControl.addTab(widget.id, labelResourceId, iconId, enableCloseButton);
|
||||||
|
TabItem item = new TabItem(id, labelResourceId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// select tab
|
||||||
|
void selectTab(string ID) {
|
||||||
|
int index = _tabControl.tabIndex(ID);
|
||||||
|
if (index != -1) {
|
||||||
|
_tabControl.selectTab(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// compound widget - contains from TabControl widget (tabs header) and TabHost (content pages)
|
||||||
|
class TabWidget : VerticalLayout {
|
||||||
|
protected TabControl _tabControl;
|
||||||
|
protected TabHost _tabHost;
|
||||||
|
this(string ID) {
|
||||||
|
super(ID);
|
||||||
|
_tabControl = new TabControl("TAB_CONTROL");
|
||||||
|
_tabHost = new TabHost("TAB_HOST", _tabControl);
|
||||||
|
addChild(_tabControl);
|
||||||
|
addChild(_tabHost);
|
||||||
|
}
|
||||||
|
/// add new tab by id and label string resource id
|
||||||
|
TabWidget addTab(Widget widget, string labelResourceId, string iconId = null, bool enableCloseButton = false) {
|
||||||
|
_tabHost.addTab(widget, labelResourceId, iconId, enableCloseButton);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// add new tab by id and label (raw value)
|
||||||
|
TabWidget addTab(Widget widget, dstring label, string iconId = null, bool enableCloseButton = false) {
|
||||||
|
_tabHost.addTab(widget, label, iconId, enableCloseButton);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// remove tab by id
|
||||||
|
TabWidget removeTab(string id) {
|
||||||
|
_tabHost.removeTab(id);
|
||||||
|
requestLayout();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// select tab
|
||||||
|
void selectTab(string ID) {
|
||||||
|
_tabHost.selectTab(ID);
|
||||||
|
}
|
||||||
|
}
|
|
@ -123,12 +123,14 @@ class Widget {
|
||||||
/// returns widget id, null if not set
|
/// returns widget id, null if not set
|
||||||
@property string id() const { return _id; }
|
@property string id() const { return _id; }
|
||||||
/// set widget id
|
/// set widget id
|
||||||
@property void id(string id) { _id = id; }
|
@property Widget id(string id) { _id = id; return this; }
|
||||||
/// compare widget id with specified value, returs true if matches
|
/// compare widget id with specified value, returs true if matches
|
||||||
bool compareId(string id) const { return (_id !is null) && id.equal(_id); }
|
bool compareId(string id) const { return (_id !is null) && id.equal(_id); }
|
||||||
|
|
||||||
/// widget state (set of flags from State enum)
|
/// widget state (set of flags from State enum)
|
||||||
@property uint state() const {
|
@property uint state() const {
|
||||||
|
if ((_state & State.Parent) != 0 && _parent !is null)
|
||||||
|
return _parent.state;
|
||||||
return _state;
|
return _state;
|
||||||
}
|
}
|
||||||
/// set new widget state (set of flags from State enum)
|
/// set new widget state (set of flags from State enum)
|
||||||
|
@ -187,7 +189,7 @@ class Widget {
|
||||||
/// set background color for widget - override one from style
|
/// set background color for widget - override one from style
|
||||||
@property Widget backgroundColor(uint color) { ownStyle.backgroundColor = color; return this; }
|
@property Widget backgroundColor(uint color) { ownStyle.backgroundColor = color; return this; }
|
||||||
/// get text color (ARGB 32 bit value)
|
/// get text color (ARGB 32 bit value)
|
||||||
@property uint textColor() const { return style.textColor; }
|
@property uint textColor() const { return stateStyle.textColor; }
|
||||||
/// set text color (ARGB 32 bit value)
|
/// set text color (ARGB 32 bit value)
|
||||||
@property Widget textColor(uint value) { ownStyle.textColor = value; return this; }
|
@property Widget textColor(uint value) { ownStyle.textColor = value; return this; }
|
||||||
/// returns font face
|
/// returns font face
|
||||||
|
@ -462,9 +464,11 @@ class Widget {
|
||||||
/// returns child by index
|
/// returns child by index
|
||||||
Widget child(int index) { return null; }
|
Widget child(int index) { return null; }
|
||||||
/// adds child, returns added item
|
/// adds child, returns added item
|
||||||
Widget addChild(Widget item) { assert(false, "children not suported for this widget type"); }
|
Widget addChild(Widget item) { assert(false, "addChild: children not suported for this widget type"); }
|
||||||
/// removes child, returns added item
|
/// removes child, returns removed item
|
||||||
Widget removeChild(int index) { assert(false, "children not suported for this widget type"); }
|
Widget removeChild(int index) { assert(false, "removeChild: children not suported for this widget type"); }
|
||||||
|
/// removes child by ID, returns removed item
|
||||||
|
Widget removeChild(string id) { assert(false, "removeChild: children not suported for this widget type"); }
|
||||||
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
||||||
int childIndex(Widget item) { return -1; }
|
int childIndex(Widget item) { return -1; }
|
||||||
|
|
||||||
|
@ -568,6 +572,13 @@ struct WidgetList {
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
/// find child index for item by id, return -1 if not found
|
||||||
|
int indexOf(string id) {
|
||||||
|
for (int i = 0; i < _count; i++)
|
||||||
|
if (_list[i].compareId(id))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
/// remove item from list, return removed item
|
/// remove item from list, return removed item
|
||||||
Widget remove(int index) {
|
Widget remove(int index) {
|
||||||
assert(index >= 0 && index < _count, "child index out of range");
|
assert(index >= 0 && index < _count, "child index out of range");
|
||||||
|
@ -605,8 +616,24 @@ class WidgetGroup : Widget {
|
||||||
override Widget child(int index) { return _children.get(index); }
|
override Widget child(int index) { return _children.get(index); }
|
||||||
/// adds child, returns added item
|
/// adds child, returns added item
|
||||||
override Widget addChild(Widget item) { return _children.add(item).parent(this); }
|
override Widget addChild(Widget item) { return _children.add(item).parent(this); }
|
||||||
/// removes child, returns added item
|
/// removes child, returns removed item
|
||||||
override Widget removeChild(int index) { return _children.remove(index); }
|
override Widget removeChild(int index) {
|
||||||
|
Widget res = _children.remove(index);
|
||||||
|
if (res !is null)
|
||||||
|
res.parent = null;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/// removes child by ID, returns removed item
|
||||||
|
override Widget removeChild(string ID) {
|
||||||
|
Widget res = null;
|
||||||
|
int index = _children.indexOf(ID);
|
||||||
|
if (index < 0)
|
||||||
|
return null;
|
||||||
|
res = _children.remove(index);
|
||||||
|
if (res !is null)
|
||||||
|
res.parent = null;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
||||||
override int childIndex(Widget item) { return _children.indexOf(item); }
|
override int childIndex(Widget item) { return _children.indexOf(item); }
|
||||||
}
|
}
|
||||||
|
|