From a3d195fb8b263f2cfaebefd811992eef45ceaefc Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Fri, 14 Mar 2014 17:39:56 +0400 Subject: [PATCH] XCB version working - except libPNG version issue --- src/dlangui/widgets/widget.d | 843 ++++++++++++++++++----------------- 1 file changed, 424 insertions(+), 419 deletions(-) diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 60c0fa00..2126dc42 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -1,419 +1,424 @@ -module dlangui.widgets.widget; - -public import dlangui.core.types; -public import dlangui.widgets.styles; -public import dlangui.graphics.drawbuf; -public import dlangui.graphics.images; -public import dlangui.graphics.fonts; - -import dlangui.platforms.common.platform; - -import std.algorithm; - - -/// Visibility (see Android View Visibility) -enum Visibility : ubyte { - /// Visible on screen (default) - Visible, - /// Not visible, but occupies a space in layout - Invisible, - /// Completely hidden, as not has been added - Gone -} - -class Widget { - /// widget id - protected string _id; - /// current widget position, set by layout() - protected Rect _pos; - /// widget visibility: either Visible, Invisible, Gone - protected Visibility _visibility = Visibility.Visible; // visible by default - /// style id to lookup style in theme - protected string _styleId; - /// own copy of style - to override some of style properties, null of no properties overriden - protected Style _ownStyle; - /// width measured by measure() - protected int _measuredWidth; - /// height measured by measure() - protected int _measuredHeight; - /// true to force layout - protected bool _needLayout = true; - /// true to force redraw - protected bool _needDraw = true; - /// parent widget - protected Widget _parent; - /// window (to be used for top level widgets only!) - protected Window _window; - - this(string ID = null) { - _id = id; - } - - /// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used). - protected @property const (Style) style() const { - if (_ownStyle !is null) - return _ownStyle; - return currentTheme.get(_styleId); - } - /// enforces widget's own style - allows override some of style properties - protected @property Style ownStyle() { - if (_ownStyle is null) - _ownStyle = currentTheme.modifyStyle(_styleId); - return _ownStyle; - } - - /// returns widget id, null if not set - @property string id() const { return _styleId; } - /// set widget id - @property void id(string id) { _id = id; } - /// compare widget id with specified value, returs true if matches - bool compareId(string id) { return (_id !is null) && id.equal(_id); } - - //====================================================== - // Style related properties - - /// returns widget style id, null if not set - @property string styleId() const { return _styleId; } - /// set widget style id - @property void styleId(string id) { _styleId = id; } - /// get margins (between widget bounds and its background) - @property Rect margins() const { return style.margins; } - /// set margins for widget - override one from style - @property Widget margins(Rect rc) { ownStyle.margins = rc; return this; } - /// get padding (between background bounds and content of widget) - @property Rect padding() const { - // get max padding from style padding and background drawable padding - Rect p = style.padding; - Rect dp = style.backgroundDrawable.padding; - if (p.left < dp.left) - p.left = dp.left; - if (p.right < dp.right) - p.right = dp.right; - if (p.top < dp.top) - p.top = dp.top; - if (p.bottom < dp.bottom) - p.bottom = dp.bottom; - return p; - } - /// set padding for widget - override one from style - @property Widget padding(Rect rc) { ownStyle.padding = rc; return this; } - /// returns background color - @property uint backgroundColor() const { return style.backgroundColor; } - /// set background color for widget - override one from style - @property Widget backgroundColor(uint color) { ownStyle.backgroundColor = color; return this; } - /// get text color (ARGB 32 bit value) - @property uint textColor() const { return style.textColor; } - /// set text color (ARGB 32 bit value) - @property Widget textColor(uint value) { ownStyle.textColor = value; return this; } - /// returns font face - @property string fontFace() const { return style.fontFace; } - /// set font face for widget - override one from style - @property Widget fontFace(string face) { ownStyle.fontFace = face; return this; } - /// returns font style (italic/normal) - @property bool fontItalic() const { return style.fontItalic; } - /// set font style (italic/normal) for widget - override one from style - @property Widget fontItalic(bool italic) { ownStyle.fontStyle = italic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL; return this; } - /// returns font weight - @property ushort fontWeight() const { return style.fontWeight; } - /// set font weight for widget - override one from style - @property Widget fontWeight(ushort weight) { ownStyle.fontWeight = weight; return this; } - /// returns font size in pixels - @property ushort fontSize() const { return style.fontSize; } - /// set font size for widget - override one from style - @property Widget fontSize(ushort size) { ownStyle.fontSize = size; return this; } - /// returns font family - @property FontFamily fontFamily() const { return style.fontFamily; } - /// set font family for widget - override one from style - @property Widget fontFamily(FontFamily family) { ownStyle.fontFamily = family; return this; } - /// returns alignment (combined vertical and horizontal) - @property ubyte alignment() const { return style.alignment; } - /// sets alignment (combined vertical and horizontal) - @property Widget alignment(ubyte value) { ownStyle.alignment = value; return this; } - /// returns horizontal alignment - @property Align valign() { return cast(Align)(alignment & Align.VCenter); } - /// returns vertical alignment - @property Align halign() { return cast(Align)(alignment & Align.HCenter); } - /// returns font set for widget using style or set manually - @property FontRef font() const { return style.font; } - - /// returns widget content text (override to support this) - @property dstring text() { return ""; } - /// sets widget content text (override to support this) - @property Widget text(dstring s) { return this; } - - //================================================================== - // Layout and drawing related methods - - /// returns true if layout is required for widget and its children - @property bool needLayout() { return _needLayout; } - /// returns true if redraw is required for widget and its children - @property bool needDraw() { return _needDraw; } - /// returns measured width (calculated during measure() call) - @property measuredWidth() { return _measuredWidth; } - /// returns measured height (calculated during measure() call) - @property measuredHeight() { return _measuredHeight; } - /// returns current width of widget in pixels - @property int width() { return _pos.width; } - /// returns current height of widget in pixels - @property int height() { return _pos.height; } - /// returns min width constraint - @property int minWidth() { return style.minWidth; } - /// returns max width constraint (SIZE_UNSPECIFIED if no constraint set) - @property int maxWidth() { return style.maxWidth; } - /// returns min height constraint - @property int minHeight() { return style.minHeight; } - /// returns max height constraint (SIZE_UNSPECIFIED if no constraint set) - @property int maxHeight() { return style.maxHeight; } - - /// set max width constraint (SIZE_UNSPECIFIED for no constraint) - @property Widget maxWidth(int value) { ownStyle.maxWidth = value; return this; } - /// set max width constraint (0 for no constraint) - @property Widget minWidth(int value) { ownStyle.minWidth = value; return this; } - /// set max height constraint (SIZE_UNSPECIFIED for no constraint) - @property Widget maxHeight(int value) { ownStyle.maxHeight = value; return this; } - /// set max height constraint (0 for no constraint) - @property Widget minHeight(int value) { ownStyle.minHeight = value; return this; } - - /// returns layout width options (WRAP_CONTENT, FILL_PARENT, or some constant value) - @property int layoutWidth() { return style.layoutWidth; } - /// returns layout height options (WRAP_CONTENT, FILL_PARENT, or some constant value) - @property int layoutHeight() { return style.layoutHeight; } - /// returns layout weight (while resizing to fill parent, widget will be resized proportionally to this value) - @property int layoutWeight() { return style.layoutWeight; } - - /// sets layout width options (WRAP_CONTENT, FILL_PARENT, or some constant value) - @property Widget layoutWidth(int value) { ownStyle.layoutWidth = value; return this; } - /// sets layout height options (WRAP_CONTENT, FILL_PARENT, or some constant value) - @property Widget layoutHeight(int value) { ownStyle.layoutHeight = value; return this; } - /// sets layout weight (while resizing to fill parent, widget will be resized proportionally to this value) - @property Widget layoutWeight(int value) { ownStyle.layoutWeight = value; return this; } - - /// returns widget visibility (Visible, Invisible, Gone) - @property Visibility visibility() { return _visibility; } - /// sets widget visibility (Visible, Invisible, Gone) - @property Widget visibility(Visibility visible) { - _visibility = visible; - requestLayout(); - return this; - } - - /// request relayout of widget and its children - void requestLayout() { - _needLayout = true; - } - /// request redraw - void invalidate() { - _needDraw = true; - } - - /// helper function for implement measure() when widget's content dimensions are known - protected void measuredContent(int parentWidth, int parentHeight, int contentWidth, int contentHeight) { - if (visibility == Visibility.Gone) { - _measuredWidth = _measuredHeight = 0; - return; - } - Rect m = margins; - Rect p = padding; - // summarize margins, padding, and content size - int dx = m.left + m.right + p.left + p.right + contentWidth; - int dy = m.top + m.bottom + p.top + p.bottom + contentHeight; - // apply min/max width and height constraints - int minw = minWidth; - int maxw = maxWidth; - int minh = minHeight; - int maxh = maxHeight; - if (dx < minw) - dx = minw; - if (dy < minh) - dy = minh; - if (maxw != SIZE_UNSPECIFIED && dx > maxw) - dx = maxw; - if (maxh != SIZE_UNSPECIFIED && dy > maxh) - dy = maxh; - // apply max parent size constraint - if (parentWidth != SIZE_UNSPECIFIED && dx > parentWidth) - dx = parentWidth; - if (parentHeight != SIZE_UNSPECIFIED && dy > parentHeight) - dy = parentHeight; - _measuredWidth = dx; - _measuredHeight = dy; - } - - /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). - void measure(int parentWidth, int parentHeight) { - measuredContent(parentWidth, parentHeight, 0, 0); - } - - /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). - void layout(Rect rc) { - if (visibility == Visibility.Gone) { - return; - } - _pos = rc; - _needLayout = false; - } - /// Draw widget at its position to buffer - void onDraw(DrawBuf buf) { - if (visibility != Visibility.Visible) - return; - Rect rc = _pos; - applyMargins(rc); - DrawableRef bg = style.backgroundDrawable; - bg.drawTo(buf, rc); - applyPadding(rc); - _needDraw = false; - } - - /// Helper function: applies margins to rectangle - void applyMargins(ref Rect rc) { - Rect m = margins; - rc.left += m.left; - rc.top += m.top; - rc.bottom -= m.bottom; - rc.right -= m.right; - } - /// Helper function: applies padding to rectangle - void applyPadding(ref Rect rc) { - Rect m = padding; - rc.left += m.left; - rc.top += m.top; - rc.bottom -= m.bottom; - rc.right -= m.right; - } - /// Applies alignment for content of size sz - set rectangle rc to aligned value of content inside of initial value of rc. - void applyAlign(ref Rect rc, Point sz) { - Align va = valign; - Align ha = halign; - if (va == Align.Bottom) { - rc.top = rc.bottom - sz.y; - } else if (va == Align.VCenter) { - int dy = (rc.height - sz.y) / 2; - rc.top += dy; - rc.bottom = rc.top + sz.y; - } else { - rc.bottom = rc.top + sz.y; - } - if (ha == Align.Right) { - rc.left = rc.right - sz.x; - } else if (ha == Align.HCenter) { - int dx = (rc.width - sz.x) / 2; - rc.left += dx; - rc.right = rc.left + sz.x; - } else { - rc.right = rc.left + sz.x; - } - } - - // =========================================================== - // Widget hierarhy methods - - /// returns number of children of this widget - @property int childCount() { return 0; } - /// returns child by index - Widget child(int index) { return null; } - /// adds child, returns added item - Widget addChild(Widget item) { assert(false, "children not suported for this widget type"); } - /// removes child, returns added item - Widget removeChild(int index) { assert(false, "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 - int childIndex(Widget item) { return -1; } - - /// find child by id, returns null if not found - Widget childById(string id) { - if (compareId(id)) - return this; - // lookup children - for (int i = childCount - 1; i >= 0; i--) { - Widget res = child(i).childById(id); - if (res !is null) - return res; - } - // not found - return null; - } - /// returns parent widget, null for top level widget - @property Widget parent() { return _parent; } - /// sets parent for widget - @property void parent(Widget parent) { _parent = parent; } - /// returns window (if widget or its parent is attached to window) - @property Window window() { - Widget p = this; - while (p !is null) { - if (p._window !is null) - return p._window; - p = p.parent; - } - return null; - } - /// sets window (to be used for top level widget from Window implementation). TODO: hide it from API? - @property void window(Window window) { _window = window; } - -} - -/// widget list holder -struct WidgetList { - protected Widget[] _list; - protected int _count; - /// returns count of items - @property int count() { return _count; } - /// get item by index - Widget get(int index) { - assert(index >= 0 && index < _count, "child index out of range"); - return _list[index]; - } - /// add item to list - Widget add(Widget item) { - if (_list.length <= _count) // resize - _list.length = _list.length < 4 ? 4 : _list.length * 2; - _list[_count++] = item; - return item; - } - /// find child index for item, return -1 if not found - int indexOf(Widget item) { - for (int i = 0; i < _count; i++) - if (_list[i] == item) - return i; - return -1; - } - /// remove item from list, return removed item - Widget remove(int index) { - assert(index >= 0 && index < _count, "child index out of range"); - Widget item = _list[index]; - for (int i = index; i < _count - 1; i++) - _list[i] = _list[i + 1]; - _count--; - return item; - } - /// remove and destroy all items - void clear() { - for (int i = 0; i < _count; i++) { - destroy(_list[i]); - _list[i] = null; - } - _count = 0; - } - ~this() { - clear(); - } -} - -/// base class for widgets which have children -class WidgetGroup : Widget { - - this(string ID = null) { - super(ID); - } - - protected WidgetList _children; - - /// returns number of children of this widget - @property override int childCount() { return _children.count; } - /// returns child by index - override Widget child(int index) { return _children.get(index); } - /// adds child, returns added item - override Widget addChild(Widget item) { return _children.add(item); } - /// removes child, returns added item - override Widget removeChild(int index) { return _children.remove(index); } - /// 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); } -} +module dlangui.widgets.widget; + +public import dlangui.core.types; +public import dlangui.widgets.styles; +public import dlangui.graphics.drawbuf; +public import dlangui.graphics.images; +public import dlangui.graphics.fonts; + +import dlangui.platforms.common.platform; + +import std.algorithm; + + +/// Visibility (see Android View Visibility) +enum Visibility : ubyte { + /// Visible on screen (default) + Visible, + /// Not visible, but occupies a space in layout + Invisible, + /// Completely hidden, as not has been added + Gone +} + +class Widget { + /// widget id + protected string _id; + /// current widget position, set by layout() + protected Rect _pos; + /// widget visibility: either Visible, Invisible, Gone + protected Visibility _visibility = Visibility.Visible; // visible by default + /// style id to lookup style in theme + protected string _styleId; + /// own copy of style - to override some of style properties, null of no properties overriden + protected Style _ownStyle; + /// width measured by measure() + protected int _measuredWidth; + /// height measured by measure() + protected int _measuredHeight; + /// true to force layout + protected bool _needLayout = true; + /// true to force redraw + protected bool _needDraw = true; + /// parent widget + protected Widget _parent; + /// window (to be used for top level widgets only!) + protected Window _window; + + this(string ID = null) { + _id = id; + } + + /// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used). + protected @property const (Style) style() const { + if (_ownStyle !is null) + return _ownStyle; + return currentTheme.get(_styleId); + } + /// enforces widget's own style - allows override some of style properties + protected @property Style ownStyle() { + if (_ownStyle is null) + _ownStyle = currentTheme.modifyStyle(_styleId); + return _ownStyle; + } + + /// returns widget id, null if not set + @property string id() const { return _styleId; } + /// set widget id + @property void id(string id) { _id = id; } + /// compare widget id with specified value, returs true if matches + bool compareId(string id) { return (_id !is null) && id.equal(_id); } + + //====================================================== + // Style related properties + + /// returns widget style id, null if not set + @property string styleId() const { return _styleId; } + /// set widget style id + @property void styleId(string id) { _styleId = id; } + /// get margins (between widget bounds and its background) + @property Rect margins() const { return style.margins; } + /// set margins for widget - override one from style + @property Widget margins(Rect rc) { ownStyle.margins = rc; return this; } + /// get padding (between background bounds and content of widget) + @property Rect padding() const { + // get max padding from style padding and background drawable padding + Rect p = style.padding; + DrawableRef d = style.backgroundDrawable; + if (!d.isNull) { + Rect dp = style.backgroundDrawable.padding; + if (p.left < dp.left) + p.left = dp.left; + if (p.right < dp.right) + p.right = dp.right; + if (p.top < dp.top) + p.top = dp.top; + if (p.bottom < dp.bottom) + p.bottom = dp.bottom; + } + return p; + } + /// set padding for widget - override one from style + @property Widget padding(Rect rc) { ownStyle.padding = rc; return this; } + /// returns background color + @property uint backgroundColor() const { return style.backgroundColor; } + /// set background color for widget - override one from style + @property Widget backgroundColor(uint color) { ownStyle.backgroundColor = color; return this; } + /// get text color (ARGB 32 bit value) + @property uint textColor() const { return style.textColor; } + /// set text color (ARGB 32 bit value) + @property Widget textColor(uint value) { ownStyle.textColor = value; return this; } + /// returns font face + @property string fontFace() const { return style.fontFace; } + /// set font face for widget - override one from style + @property Widget fontFace(string face) { ownStyle.fontFace = face; return this; } + /// returns font style (italic/normal) + @property bool fontItalic() const { return style.fontItalic; } + /// set font style (italic/normal) for widget - override one from style + @property Widget fontItalic(bool italic) { ownStyle.fontStyle = italic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL; return this; } + /// returns font weight + @property ushort fontWeight() const { return style.fontWeight; } + /// set font weight for widget - override one from style + @property Widget fontWeight(ushort weight) { ownStyle.fontWeight = weight; return this; } + /// returns font size in pixels + @property ushort fontSize() const { return style.fontSize; } + /// set font size for widget - override one from style + @property Widget fontSize(ushort size) { ownStyle.fontSize = size; return this; } + /// returns font family + @property FontFamily fontFamily() const { return style.fontFamily; } + /// set font family for widget - override one from style + @property Widget fontFamily(FontFamily family) { ownStyle.fontFamily = family; return this; } + /// returns alignment (combined vertical and horizontal) + @property ubyte alignment() const { return style.alignment; } + /// sets alignment (combined vertical and horizontal) + @property Widget alignment(ubyte value) { ownStyle.alignment = value; return this; } + /// returns horizontal alignment + @property Align valign() { return cast(Align)(alignment & Align.VCenter); } + /// returns vertical alignment + @property Align halign() { return cast(Align)(alignment & Align.HCenter); } + /// returns font set for widget using style or set manually + @property FontRef font() const { return style.font; } + + /// returns widget content text (override to support this) + @property dstring text() { return ""; } + /// sets widget content text (override to support this) + @property Widget text(dstring s) { return this; } + + //================================================================== + // Layout and drawing related methods + + /// returns true if layout is required for widget and its children + @property bool needLayout() { return _needLayout; } + /// returns true if redraw is required for widget and its children + @property bool needDraw() { return _needDraw; } + /// returns measured width (calculated during measure() call) + @property measuredWidth() { return _measuredWidth; } + /// returns measured height (calculated during measure() call) + @property measuredHeight() { return _measuredHeight; } + /// returns current width of widget in pixels + @property int width() { return _pos.width; } + /// returns current height of widget in pixels + @property int height() { return _pos.height; } + /// returns min width constraint + @property int minWidth() { return style.minWidth; } + /// returns max width constraint (SIZE_UNSPECIFIED if no constraint set) + @property int maxWidth() { return style.maxWidth; } + /// returns min height constraint + @property int minHeight() { return style.minHeight; } + /// returns max height constraint (SIZE_UNSPECIFIED if no constraint set) + @property int maxHeight() { return style.maxHeight; } + + /// set max width constraint (SIZE_UNSPECIFIED for no constraint) + @property Widget maxWidth(int value) { ownStyle.maxWidth = value; return this; } + /// set max width constraint (0 for no constraint) + @property Widget minWidth(int value) { ownStyle.minWidth = value; return this; } + /// set max height constraint (SIZE_UNSPECIFIED for no constraint) + @property Widget maxHeight(int value) { ownStyle.maxHeight = value; return this; } + /// set max height constraint (0 for no constraint) + @property Widget minHeight(int value) { ownStyle.minHeight = value; return this; } + + /// returns layout width options (WRAP_CONTENT, FILL_PARENT, or some constant value) + @property int layoutWidth() { return style.layoutWidth; } + /// returns layout height options (WRAP_CONTENT, FILL_PARENT, or some constant value) + @property int layoutHeight() { return style.layoutHeight; } + /// returns layout weight (while resizing to fill parent, widget will be resized proportionally to this value) + @property int layoutWeight() { return style.layoutWeight; } + + /// sets layout width options (WRAP_CONTENT, FILL_PARENT, or some constant value) + @property Widget layoutWidth(int value) { ownStyle.layoutWidth = value; return this; } + /// sets layout height options (WRAP_CONTENT, FILL_PARENT, or some constant value) + @property Widget layoutHeight(int value) { ownStyle.layoutHeight = value; return this; } + /// sets layout weight (while resizing to fill parent, widget will be resized proportionally to this value) + @property Widget layoutWeight(int value) { ownStyle.layoutWeight = value; return this; } + + /// returns widget visibility (Visible, Invisible, Gone) + @property Visibility visibility() { return _visibility; } + /// sets widget visibility (Visible, Invisible, Gone) + @property Widget visibility(Visibility visible) { + _visibility = visible; + requestLayout(); + return this; + } + + /// request relayout of widget and its children + void requestLayout() { + _needLayout = true; + } + /// request redraw + void invalidate() { + _needDraw = true; + } + + /// helper function for implement measure() when widget's content dimensions are known + protected void measuredContent(int parentWidth, int parentHeight, int contentWidth, int contentHeight) { + if (visibility == Visibility.Gone) { + _measuredWidth = _measuredHeight = 0; + return; + } + Rect m = margins; + Rect p = padding; + // summarize margins, padding, and content size + int dx = m.left + m.right + p.left + p.right + contentWidth; + int dy = m.top + m.bottom + p.top + p.bottom + contentHeight; + // apply min/max width and height constraints + int minw = minWidth; + int maxw = maxWidth; + int minh = minHeight; + int maxh = maxHeight; + if (dx < minw) + dx = minw; + if (dy < minh) + dy = minh; + if (maxw != SIZE_UNSPECIFIED && dx > maxw) + dx = maxw; + if (maxh != SIZE_UNSPECIFIED && dy > maxh) + dy = maxh; + // apply max parent size constraint + if (parentWidth != SIZE_UNSPECIFIED && dx > parentWidth) + dx = parentWidth; + if (parentHeight != SIZE_UNSPECIFIED && dy > parentHeight) + dy = parentHeight; + _measuredWidth = dx; + _measuredHeight = dy; + } + + /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). + void measure(int parentWidth, int parentHeight) { + measuredContent(parentWidth, parentHeight, 0, 0); + } + + /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). + void layout(Rect rc) { + if (visibility == Visibility.Gone) { + return; + } + _pos = rc; + _needLayout = false; + } + /// Draw widget at its position to buffer + void onDraw(DrawBuf buf) { + if (visibility != Visibility.Visible) + return; + Rect rc = _pos; + applyMargins(rc); + DrawableRef bg = style.backgroundDrawable; + if (!bg.isNull) { + bg.drawTo(buf, rc); + } + applyPadding(rc); + _needDraw = false; + } + + /// Helper function: applies margins to rectangle + void applyMargins(ref Rect rc) { + Rect m = margins; + rc.left += m.left; + rc.top += m.top; + rc.bottom -= m.bottom; + rc.right -= m.right; + } + /// Helper function: applies padding to rectangle + void applyPadding(ref Rect rc) { + Rect m = padding; + rc.left += m.left; + rc.top += m.top; + rc.bottom -= m.bottom; + rc.right -= m.right; + } + /// Applies alignment for content of size sz - set rectangle rc to aligned value of content inside of initial value of rc. + void applyAlign(ref Rect rc, Point sz) { + Align va = valign; + Align ha = halign; + if (va == Align.Bottom) { + rc.top = rc.bottom - sz.y; + } else if (va == Align.VCenter) { + int dy = (rc.height - sz.y) / 2; + rc.top += dy; + rc.bottom = rc.top + sz.y; + } else { + rc.bottom = rc.top + sz.y; + } + if (ha == Align.Right) { + rc.left = rc.right - sz.x; + } else if (ha == Align.HCenter) { + int dx = (rc.width - sz.x) / 2; + rc.left += dx; + rc.right = rc.left + sz.x; + } else { + rc.right = rc.left + sz.x; + } + } + + // =========================================================== + // Widget hierarhy methods + + /// returns number of children of this widget + @property int childCount() { return 0; } + /// returns child by index + Widget child(int index) { return null; } + /// adds child, returns added item + Widget addChild(Widget item) { assert(false, "children not suported for this widget type"); } + /// removes child, returns added item + Widget removeChild(int index) { assert(false, "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 + int childIndex(Widget item) { return -1; } + + /// find child by id, returns null if not found + Widget childById(string id) { + if (compareId(id)) + return this; + // lookup children + for (int i = childCount - 1; i >= 0; i--) { + Widget res = child(i).childById(id); + if (res !is null) + return res; + } + // not found + return null; + } + /// returns parent widget, null for top level widget + @property Widget parent() { return _parent; } + /// sets parent for widget + @property void parent(Widget parent) { _parent = parent; } + /// returns window (if widget or its parent is attached to window) + @property Window window() { + Widget p = this; + while (p !is null) { + if (p._window !is null) + return p._window; + p = p.parent; + } + return null; + } + /// sets window (to be used for top level widget from Window implementation). TODO: hide it from API? + @property void window(Window window) { _window = window; } + +} + +/// widget list holder +struct WidgetList { + protected Widget[] _list; + protected int _count; + /// returns count of items + @property int count() { return _count; } + /// get item by index + Widget get(int index) { + assert(index >= 0 && index < _count, "child index out of range"); + return _list[index]; + } + /// add item to list + Widget add(Widget item) { + if (_list.length <= _count) // resize + _list.length = _list.length < 4 ? 4 : _list.length * 2; + _list[_count++] = item; + return item; + } + /// find child index for item, return -1 if not found + int indexOf(Widget item) { + for (int i = 0; i < _count; i++) + if (_list[i] == item) + return i; + return -1; + } + /// remove item from list, return removed item + Widget remove(int index) { + assert(index >= 0 && index < _count, "child index out of range"); + Widget item = _list[index]; + for (int i = index; i < _count - 1; i++) + _list[i] = _list[i + 1]; + _count--; + return item; + } + /// remove and destroy all items + void clear() { + for (int i = 0; i < _count; i++) { + destroy(_list[i]); + _list[i] = null; + } + _count = 0; + } + ~this() { + clear(); + } +} + +/// base class for widgets which have children +class WidgetGroup : Widget { + + this(string ID = null) { + super(ID); + } + + protected WidgetList _children; + + /// returns number of children of this widget + @property override int childCount() { return _children.count; } + /// returns child by index + override Widget child(int index) { return _children.get(index); } + /// adds child, returns added item + override Widget addChild(Widget item) { return _children.add(item); } + /// removes child, returns added item + override Widget removeChild(int index) { return _children.remove(index); } + /// 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); } +}