diff --git a/dlanguilib.dproj b/dlanguilib.dproj index 06974812..c4c38cea 100644 --- a/dlanguilib.dproj +++ b/dlanguilib.dproj @@ -36,7 +36,8 @@ dlanguilib obj/Release true - Executable + StaticLibrary + -version=USE_OPENGL true @@ -45,7 +46,7 @@ obj/Unittest dlanguilib true - Executable + StaticLibrary @@ -61,9 +62,6 @@ - - - 3rdparty\DerelictFT\ft.d diff --git a/examples/example1/main.d b/examples/example1/main.d index 6365f7af..3d2328f5 100644 --- a/examples/example1/main.d +++ b/examples/example1/main.d @@ -5,7 +5,7 @@ import std.stdio; import std.conv; version (linux) { - pragma(lib, "png"); + //pragma(lib, "png"); pragma(lib, "xcb"); pragma(lib, "xcb-shm"); pragma(lib, "xcb-image"); diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 0302be4e..cbf20c0a 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -80,6 +80,7 @@ class Window { _mainWidget.layout(Rect(0, 0, _dx, _dy)); long layoutEnd = currentTimeMillis; Log.d("layout took ", layoutEnd - measureEnd, " ms"); + checkUpdateNeeded(needDraw, needLayout, animationActive); } long drawStart = currentTimeMillis; _mainWidget.onDraw(buf); diff --git a/src/dlangui/platforms/x11/x11app.d b/src/dlangui/platforms/x11/x11app.d index 3c468cbe..ebcadce4 100644 --- a/src/dlangui/platforms/x11/x11app.d +++ b/src/dlangui/platforms/x11/x11app.d @@ -27,7 +27,14 @@ version(linux) { import derelict.opengl3.gl3; import derelict.opengl3.glx; - + +// pragma(lib, "xcb"); +// pragma(lib, "xcb-shm"); +// pragma(lib, "xcb-image"); +// pragma(lib, "X11-xcb"); +// pragma(lib, "X11"); +// pragma(lib, "dl"); + extern (System) xcb_connection_t *XGetXCBConnection(std.c.linux.X11.Xlib.Display *dpy); enum XEventQueueOwner { XlibOwnsEventQueue = 0, XCBOwnsEventQueue }; @@ -160,12 +167,20 @@ version(linux) { windowCaption = _caption; return true; } - + + private int _imageDx; + private int _imageDy; void createImage() { - Log.i("CRXCBScreen::createImage ", _dx, "x", _dy); - if (_image) + if (_image) { + if (_imageDx == _imageDx && _imageDy == _dy) + return; // already have image of proper size + Log.i("CRXCBScreen::createImage - destroying existing image"); xcb_image_destroy(_image); - _image = null; + _image = null; + } + _imageDx = _dx; + _imageDy = _dy; + Log.i("CRXCBScreen::createImage ", _dx, "x", _dy); xcb_shm_query_version_reply_t * rep_shm; rep_shm = xcb_shm_query_version_reply (_xcbconnection, xcb_shm_query_version(_xcbconnection), @@ -403,9 +418,9 @@ version(linux) { _drawbuf = new ColorDrawBuf(_dx, _dy); _drawbuf.resize(_dx, _dy); _drawbuf.fill(_backgroundColor); - Log.d("calling createImage"); + //Log.d("calling createImage"); createImage(); - Log.d("done createImage"); + //Log.d("done createImage"); onDraw(_drawbuf); draw(_drawbuf); /* diff --git a/src/dlangui/widgets/controls.d b/src/dlangui/widgets/controls.d index a1c66cb3..7c21889e 100644 --- a/src/dlangui/widgets/controls.d +++ b/src/dlangui/widgets/controls.d @@ -456,8 +456,32 @@ class ScrollBar : AbstractSlider, OnClickHandler { } measuredContent(parentWidth, parentHeight, sz.x, sz.y); } + + override protected void onPositionChanged() { + if (!needLayout) + layoutButtons(); + } - protected void layoutButtons(Rect irc) { + protected void layoutButtons() { + Rect irc = _scrollArea; + if (_orientation == Orientation.Vertical) { + // vertical + int spaceBackSize, spaceForwardSize, indicatorSize; + bool indicatorVisible = calcButtonSizes(_scrollArea.height, spaceBackSize, spaceForwardSize, indicatorSize); + irc.top += spaceBackSize; + irc.bottom -= spaceForwardSize; + layoutButtons(irc); + } else { + // horizontal + int spaceBackSize, spaceForwardSize, indicatorSize; + bool indicatorVisible = calcButtonSizes(_scrollArea.width, spaceBackSize, spaceForwardSize, indicatorSize); + irc.left += spaceBackSize; + irc.right -= spaceForwardSize; + layoutButtons(irc); + } + } + + protected void layoutButtons(Rect irc) { Rect r; _indicator.visibility = Visibility.Visible; if (_orientation == Orientation.Vertical) { @@ -500,6 +524,7 @@ class ScrollBar : AbstractSlider, OnClickHandler { } override void layout(Rect rc) { + _needLayout = false; applyMargins(rc); applyPadding(rc); Rect r; @@ -519,12 +544,6 @@ class ScrollBar : AbstractSlider, OnClickHandler { r.top = backbtnpos; r.bottom = fwdbtnpos; _scrollArea = r; - int spaceBackSize, spaceForwardSize, indicatorSize; - bool indicatorVisible = calcButtonSizes(r.height, spaceBackSize, spaceForwardSize, indicatorSize); - Rect irc = r; - irc.top += spaceBackSize; - irc.bottom -= spaceForwardSize; - layoutButtons(irc); } else { // horizontal int backbtnpos = rc.left + _btnSize; @@ -540,15 +559,9 @@ class ScrollBar : AbstractSlider, OnClickHandler { r.left = backbtnpos; r.right = fwdbtnpos; _scrollArea = r; - int spaceBackSize, spaceForwardSize, indicatorSize; - bool indicatorVisible = calcButtonSizes(r.width, spaceBackSize, spaceForwardSize, indicatorSize); - Rect irc = r; - irc.left += spaceBackSize; - irc.right -= spaceForwardSize; - layoutButtons(irc); } + layoutButtons(); _pos = rc; - _needLayout = false; } override bool onClick(Widget source) { diff --git a/src/dlangui/widgets/layouts.d b/src/dlangui/widgets/layouts.d index 6f1c8936..812427d1 100644 --- a/src/dlangui/widgets/layouts.d +++ b/src/dlangui/widgets/layouts.d @@ -1,378 +1,377 @@ -module dlangui.widgets.layouts; - -public import dlangui.widgets.widget; - -/// helper for layouts -struct LayoutItem { - Widget _widget; - Orientation _orientation; - int _measuredSize; // primary size for orientation - int _secondarySize; // other measured size - int _layoutSize; // layout size for primary dimension - int _minSize; // min size for primary dimension - int _maxSize; // max size for primary dimension - int _weight; // weight - bool _fillParent; - @property int measuredSize() { return _measuredSize; } - @property int minSize() { return _measuredSize; } - @property int maxSize() { return _maxSize; } - @property int layoutSize() { return _layoutSize; } - @property int secondarySize() { return _layoutSize; } - @property bool fillParent() { return _fillParent; } - @property int weight() { return _weight; } - // just to help GC - void clear() { - _widget = null; - } - /// sets item for widget - void set(Widget widget, Orientation orientation) { - _widget = widget; - _orientation = orientation; - } - /// set item and measure it - void measure(int parentWidth, int parentHeight) { - _widget.measure(parentWidth, parentHeight); - _weight = _widget.layoutWeight; - if (_orientation == Orientation.Horizontal) { - _secondarySize = _widget.measuredHeight; - _measuredSize = _widget.measuredWidth; - _minSize = _widget.minWidth; - _maxSize = _widget.maxWidth; - _layoutSize = _widget.layoutWidth; - } else { - _secondarySize = _widget.measuredWidth; - _measuredSize = _widget.measuredHeight; - _minSize = _widget.minHeight; - _maxSize = _widget.maxHeight; - _layoutSize = _widget.layoutHeight; - } - _fillParent = _layoutSize == FILL_PARENT; - } - void layout(ref Rect rc) { - _widget.layout(rc); - } -} - -/// helper class for layouts -class LayoutItems { - Orientation _orientation; - LayoutItem[] _list; - int _count; - int _totalSize; - int _maxSecondarySize; - Point _measureParentSize; - - int _layoutWidth; - int _layoutHeight; - - void setLayoutParams(Orientation orientation, int layoutWidth, int layoutHeight) { - _orientation = orientation; - _layoutWidth = layoutWidth; - _layoutHeight = layoutHeight; - } - - /// fill widget layout list with Visible or Invisible items, measure them - Point measure(int parentWidth, int parentHeight) { - _totalSize = 0; - _maxSecondarySize = 0; - _measureParentSize.x = parentWidth; - _measureParentSize.y = parentHeight; - // measure - for (int i = 0; i < _count; i++) { - LayoutItem * item = &_list[i]; - item.measure(parentWidth, parentHeight); - if (_maxSecondarySize < item._secondarySize) - _maxSecondarySize = item._secondarySize; - _totalSize += item._measuredSize; - } - return _orientation == Orientation.Horizontal ? Point(_totalSize, _maxSecondarySize) : Point(_maxSecondarySize, _totalSize); - } - - /// fill widget layout list with Visible or Invisible items, measure them - void setWidgets(ref WidgetList widgets) { - // remove old items, if any - clear(); - // reserve space - if (_list.length < widgets.count) - _list.length = widgets.count; - // copy - for (int i = 0; i < widgets.count; i++) { - Widget item = widgets.get(i); - if (item.visibility == Visibility.Gone) - continue; - _list[_count++].set(item, _orientation); - } - } - - void layout(Rect rc) { - // measure again - available area could be changed - if (_measureParentSize.x != rc.width || _measureParentSize.y != rc.height) - measure(rc.width, rc.height); - int contentSecondarySize = 0; - int contentHeight = 0; - int totalSize = 0; - int delta = 0; - int resizableSize = 0; - int resizableWeight = 0; - int nonresizableSize = 0; - int nonresizableWeight = 0; - int maxItem = 0; // max item dimention - // calc total size - int visibleCount = cast(int)_list.length; - for (int i = 0; i < _count; i++) { - LayoutItem * item = &_list[i]; - int weight = item.weight; - int size = item.measuredSize; - totalSize += size; - if (maxItem < item.secondarySize) - maxItem = item.secondarySize; - if (item.fillParent) { - resizableWeight += weight; - resizableSize += size * weight; - } else { - nonresizableWeight += weight; - nonresizableSize += size * weight; - } - } - if (_orientation == Orientation.Vertical) { - if (_layoutWidth == WRAP_CONTENT && maxItem < rc.width) - contentSecondarySize = maxItem; - else - contentSecondarySize = rc.width; - if (_layoutHeight == FILL_PARENT || totalSize > rc.height) - delta = rc.height - totalSize; // total space to add to fit - } else { - if (_layoutHeight == WRAP_CONTENT && maxItem < rc.height) - contentSecondarySize = maxItem; - else - contentSecondarySize = rc.height; - if (_layoutWidth == FILL_PARENT || totalSize > rc.width) - delta = rc.width - totalSize; // total space to add to fit - } - // calculate resize options and scale - bool needForceResize = false; - bool needResize = false; - int scaleFactor = 10000; // per weight unit - if (delta != 0 && visibleCount > 0) { - // need resize of some children - needResize = true; - // resize all if need to shrink or only resizable are too small to correct delta - needForceResize = delta < 0 || resizableWeight == 0; // || resizableSize * 2 / 3 < delta; // do we need resize non-FILL_PARENT items? - // calculate scale factor: weight / delta * 10000 - if (needForceResize) - scaleFactor = 10000 * delta / (nonresizableSize + resizableSize); - else - scaleFactor = 10000 * delta / resizableSize; - } - //Log.d("VerticalLayout delta=", delta, ", nonres=", nonresizableWeight, ", res=", resizableWeight, ", scale=", scaleFactor); - // find last resized - to allow fill space 1 pixel accurate - int lastResized = -1; - for (int i = 0; i < _count; i++) { - LayoutItem * item = &_list[i]; - if (item.fillParent || needForceResize) { - lastResized = i; - } - } - // final resize and layout of children - int position = 0; - int deltaTotal = 0; - for (int i = 0; i < _count; i++) { - LayoutItem * item = &_list[i]; - int layoutSize = item.layoutSize; - int weight = item.weight; - int size = item.measuredSize; - if (needResize && (layoutSize == FILL_PARENT || needForceResize)) { - // do resize - int correction = scaleFactor * weight * size / 10000; - deltaTotal += correction; - // for last resized, apply additional correction to resolve calculation inaccuracy - if (i == lastResized) { - correction += delta - deltaTotal; - } - size += correction; - } - // apply size - Rect childRect = rc; - if (_orientation == Orientation.Vertical) { - // Vertical - childRect.top += position; - childRect.bottom = childRect.top + size; - childRect.right = childRect.left + contentSecondarySize; - item.layout(childRect); - } else { - // Horizontal - childRect.left += position; - childRect.right = childRect.left + size; - childRect.bottom = childRect.top + contentSecondarySize; - item.layout(childRect); - } - position += size; - } - } - - void clear() { - for (int i = 0; i < _count; i++) - _list[i].clear(); - _count = 0; - } - ~this() { - clear(); - } -} - -class LinearLayout : WidgetGroup { - protected Orientation _orientation = Orientation.Vertical; - /// returns linear layout orientation (Vertical, Horizontal) - @property Orientation orientation() { return _orientation; } - /// sets linear layout orientation - @property LinearLayout orientation(Orientation value) { _orientation = value; requestLayout(); return this; } - - this(string ID = null) { - super(ID); - _layoutItems = new LayoutItems(); - } - - LayoutItems _layoutItems; - /// 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 - _layoutItems.setLayoutParams(orientation, layoutWidth, layoutHeight); - _layoutItems.setWidgets(_children); - Point sz = _layoutItems.measure(pwidth, pheight); - 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); - _layoutItems.layout(rc); - _needLayout = false; - } - /// 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); - auto saver = 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); - } - } - -} - -class VerticalLayout : LinearLayout { - this(string ID = null) { - super(ID); - orientation = Orientation.Vertical; - } -} - -class HorizontalLayout : LinearLayout { - this(string ID = null) { - super(ID); - orientation = Orientation.Horizontal; - } -} - -/// 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); - auto saver = 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; - } -} +module dlangui.widgets.layouts; + +public import dlangui.widgets.widget; + +/// helper for layouts +struct LayoutItem { + Widget _widget; + Orientation _orientation; + int _measuredSize; // primary size for orientation + int _secondarySize; // other measured size + int _layoutSize; // layout size for primary dimension + int _minSize; // min size for primary dimension + int _maxSize; // max size for primary dimension + int _weight; // weight + bool _fillParent; + @property int measuredSize() { return _measuredSize; } + @property int minSize() { return _measuredSize; } + @property int maxSize() { return _maxSize; } + @property int layoutSize() { return _layoutSize; } + @property int secondarySize() { return _layoutSize; } + @property bool fillParent() { return _fillParent; } + @property int weight() { return _weight; } + // just to help GC + void clear() { + _widget = null; + } + /// sets item for widget + void set(Widget widget, Orientation orientation) { + _widget = widget; + _orientation = orientation; + } + /// set item and measure it + void measure(int parentWidth, int parentHeight) { + _widget.measure(parentWidth, parentHeight); + _weight = _widget.layoutWeight; + if (_orientation == Orientation.Horizontal) { + _secondarySize = _widget.measuredHeight; + _measuredSize = _widget.measuredWidth; + _minSize = _widget.minWidth; + _maxSize = _widget.maxWidth; + _layoutSize = _widget.layoutWidth; + } else { + _secondarySize = _widget.measuredWidth; + _measuredSize = _widget.measuredHeight; + _minSize = _widget.minHeight; + _maxSize = _widget.maxHeight; + _layoutSize = _widget.layoutHeight; + } + _fillParent = _layoutSize == FILL_PARENT; + } + void layout(ref Rect rc) { + _widget.layout(rc); + } +} + +/// helper class for layouts +class LayoutItems { + Orientation _orientation; + LayoutItem[] _list; + int _count; + int _totalSize; + int _maxSecondarySize; + Point _measureParentSize; + + int _layoutWidth; + int _layoutHeight; + + void setLayoutParams(Orientation orientation, int layoutWidth, int layoutHeight) { + _orientation = orientation; + _layoutWidth = layoutWidth; + _layoutHeight = layoutHeight; + } + + /// fill widget layout list with Visible or Invisible items, measure them + Point measure(int parentWidth, int parentHeight) { + _totalSize = 0; + _maxSecondarySize = 0; + _measureParentSize.x = parentWidth; + _measureParentSize.y = parentHeight; + // measure + for (int i = 0; i < _count; i++) { + LayoutItem * item = &_list[i]; + item.measure(parentWidth, parentHeight); + if (_maxSecondarySize < item._secondarySize) + _maxSecondarySize = item._secondarySize; + _totalSize += item._measuredSize; + } + return _orientation == Orientation.Horizontal ? Point(_totalSize, _maxSecondarySize) : Point(_maxSecondarySize, _totalSize); + } + + /// fill widget layout list with Visible or Invisible items, measure them + void setWidgets(ref WidgetList widgets) { + // remove old items, if any + clear(); + // reserve space + if (_list.length < widgets.count) + _list.length = widgets.count; + // copy + for (int i = 0; i < widgets.count; i++) { + Widget item = widgets.get(i); + if (item.visibility == Visibility.Gone) + continue; + _list[_count++].set(item, _orientation); + } + } + + void layout(Rect rc) { + // measure again - available area could be changed + if (_measureParentSize.x != rc.width || _measureParentSize.y != rc.height) + measure(rc.width, rc.height); + int contentSecondarySize = 0; + int contentHeight = 0; + int totalSize = 0; + int delta = 0; + int resizableSize = 0; + int resizableWeight = 0; + int nonresizableSize = 0; + int nonresizableWeight = 0; + int maxItem = 0; // max item dimention + // calc total size + int visibleCount = cast(int)_list.length; + for (int i = 0; i < _count; i++) { + LayoutItem * item = &_list[i]; + int weight = item.weight; + int size = item.measuredSize; + totalSize += size; + if (maxItem < item.secondarySize) + maxItem = item.secondarySize; + if (item.fillParent) { + resizableWeight += weight; + resizableSize += size * weight; + } else { + nonresizableWeight += weight; + nonresizableSize += size * weight; + } + } + if (_orientation == Orientation.Vertical) { + if (_layoutWidth == WRAP_CONTENT && maxItem < rc.width) + contentSecondarySize = maxItem; + else + contentSecondarySize = rc.width; + if (_layoutHeight == FILL_PARENT || totalSize > rc.height) + delta = rc.height - totalSize; // total space to add to fit + } else { + if (_layoutHeight == WRAP_CONTENT && maxItem < rc.height) + contentSecondarySize = maxItem; + else + contentSecondarySize = rc.height; + if (_layoutWidth == FILL_PARENT || totalSize > rc.width) + delta = rc.width - totalSize; // total space to add to fit + } + // calculate resize options and scale + bool needForceResize = false; + bool needResize = false; + int scaleFactor = 10000; // per weight unit + if (delta != 0 && visibleCount > 0) { + // need resize of some children + needResize = true; + // resize all if need to shrink or only resizable are too small to correct delta + needForceResize = delta < 0 || resizableWeight == 0; // || resizableSize * 2 / 3 < delta; // do we need resize non-FILL_PARENT items? + // calculate scale factor: weight / delta * 10000 + if (needForceResize) + scaleFactor = 10000 * delta / (nonresizableSize + resizableSize); + else + scaleFactor = 10000 * delta / resizableSize; + } + //Log.d("VerticalLayout delta=", delta, ", nonres=", nonresizableWeight, ", res=", resizableWeight, ", scale=", scaleFactor); + // find last resized - to allow fill space 1 pixel accurate + int lastResized = -1; + for (int i = 0; i < _count; i++) { + LayoutItem * item = &_list[i]; + if (item.fillParent || needForceResize) { + lastResized = i; + } + } + // final resize and layout of children + int position = 0; + int deltaTotal = 0; + for (int i = 0; i < _count; i++) { + LayoutItem * item = &_list[i]; + int layoutSize = item.layoutSize; + int weight = item.weight; + int size = item.measuredSize; + if (needResize && (layoutSize == FILL_PARENT || needForceResize)) { + // do resize + int correction = scaleFactor * weight * size / 10000; + deltaTotal += correction; + // for last resized, apply additional correction to resolve calculation inaccuracy + if (i == lastResized) { + correction += delta - deltaTotal; + } + size += correction; + } + // apply size + Rect childRect = rc; + if (_orientation == Orientation.Vertical) { + // Vertical + childRect.top += position; + childRect.bottom = childRect.top + size; + childRect.right = childRect.left + contentSecondarySize; + item.layout(childRect); + } else { + // Horizontal + childRect.left += position; + childRect.right = childRect.left + size; + childRect.bottom = childRect.top + contentSecondarySize; + item.layout(childRect); + } + position += size; + } + } + + void clear() { + for (int i = 0; i < _count; i++) + _list[i].clear(); + _count = 0; + } + ~this() { + clear(); + } +} + +class LinearLayout : WidgetGroup { + protected Orientation _orientation = Orientation.Vertical; + /// returns linear layout orientation (Vertical, Horizontal) + @property Orientation orientation() { return _orientation; } + /// sets linear layout orientation + @property LinearLayout orientation(Orientation value) { _orientation = value; requestLayout(); return this; } + + this(string ID = null) { + super(ID); + _layoutItems = new LayoutItems(); + } + + LayoutItems _layoutItems; + /// 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 + _layoutItems.setLayoutParams(orientation, layoutWidth, layoutHeight); + _layoutItems.setWidgets(_children); + Point sz = _layoutItems.measure(pwidth, pheight); + 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); + _layoutItems.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); + auto saver = 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); + } + } + +} + +class VerticalLayout : LinearLayout { + this(string ID = null) { + super(ID); + orientation = Orientation.Vertical; + } +} + +class HorizontalLayout : LinearLayout { + this(string ID = null) { + super(ID); + orientation = Orientation.Horizontal; + } +} + +/// 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) { + _needLayout = false; + if (visibility == Visibility.Gone) { + 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); + auto saver = 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; + } +} diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index 99f58e07..0a7b94fe 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -1,403 +1,402 @@ -module dlangui.widgets.lists; - -import dlangui.widgets.widget; -import dlangui.widgets.controls; - -/// list widget adapter provides items for list widgets -interface ListAdapter { - /// returns number of widgets in list - @property int itemCount(); - /// return list item widget by item index - Widget itemWidget(int index); -} - -/// List adapter for simple list of widget instances -class WidgetListAdapter : ListAdapter { - WidgetList _widgets; - /// list of widgets to display - @property ref WidgetList widgets() { return _widgets; } - /// returns number of widgets in list - @property override int itemCount() { - return _widgets.count; - } - /// return list item widget by item index - override Widget itemWidget(int index) { - return _widgets.get(index); - } -} - -/// List -class ListWidget : WidgetGroup, OnScrollHandler { - protected Orientation _orientation = Orientation.Vertical; - /// returns linear layout orientation (Vertical, Horizontal) - @property Orientation orientation() { return _orientation; } - /// sets linear layout orientation - @property ListWidget orientation(Orientation value) { - _orientation = value; - _scrollbar.orientation = value; - requestLayout(); - return this; - } - - protected Rect[] _itemRects; - protected Point[] _itemSizes; - protected bool _needScrollbar; - protected Point _sbsz; // scrollbar size - protected ScrollBar _scrollbar; - protected int _lastMeasureWidth; - protected int _lastMeasureHeight; - - /// first visible item index - protected int _firstVisibleItem; - /// scroll position - offset of scroll area - protected int _scrollPosition; - /// maximum scroll position - protected int _maxScrollPosition; - /// client area rectangle (counting padding, margins, and scrollbar) - protected Rect _clientRc; - /// total height of all items for Vertical orientation, or width for Horizontal - protected int _totalSize; - - /// returns rectangle for item (not scrolled, first item starts at 0,0) - Rect itemRectNoScroll(int index) { - Rect res; - res = _itemRects[index]; - return res; - } - - /// returns rectangle for item (scrolled) - Rect itemRect(int index) { - Rect res = itemRectNoScroll(index); - if (_orientation == Orientation.Horizontal) { - res.left += _scrollPosition; - res.right += _scrollPosition; - } else { - res.top += _scrollPosition; - res.bottom += _scrollPosition; - } - return res; - } - - /// returns item index by 0-based offset from top/left of list content - int itemByPosition(int pos) { - return 0; - } - - protected ListAdapter _adapter; - /// when true, need to destroy adapter on list destroy - protected bool _ownAdapter; - - /// get adapter - @property ListAdapter adapter() { return _adapter; } - /// set adapter - @property ListWidget adapter(ListAdapter adapter) { - if (_adapter !is null && _ownAdapter) - destroy(_adapter); - _adapter = adapter; - _ownAdapter = false; - onAdapterChanged(); - return this; - } - /// set adapter - @property ListWidget ownAdapter(ListAdapter adapter) { - if (_adapter !is null && _ownAdapter) - destroy(_adapter); - _adapter = adapter; - _ownAdapter = true; - onAdapterChanged(); - return this; - } - - /// returns number of widgets in list - @property int itemCount() { - if (_adapter !is null) - return _adapter.itemCount; - return 0; - } - - /// return list item widget by item index - Widget itemWidget(int index) { - if (_adapter !is null) - return _adapter.itemWidget(index); - return null; - } - - void onAdapterChanged() { - requestLayout(); - } - - this(string ID = null, Orientation orientation = Orientation.Vertical) { - super(ID); - _orientation = orientation; - _scrollbar = new ScrollBar("listscroll", orientation); - _scrollbar.visibility = Visibility.Gone; - _scrollbar.onScrollEventListener = &onScrollEvent; - addChild(_scrollbar); - } - - ~this() { - if (_adapter !is null && _ownAdapter) - destroy(_adapter); - _adapter = null; - } - +module dlangui.widgets.lists; + +import dlangui.widgets.widget; +import dlangui.widgets.controls; + +/// list widget adapter provides items for list widgets +interface ListAdapter { + /// returns number of widgets in list + @property int itemCount(); + /// return list item widget by item index + Widget itemWidget(int index); +} + +/// List adapter for simple list of widget instances +class WidgetListAdapter : ListAdapter { + WidgetList _widgets; + /// list of widgets to display + @property ref WidgetList widgets() { return _widgets; } + /// returns number of widgets in list + @property override int itemCount() { + return _widgets.count; + } + /// return list item widget by item index + override Widget itemWidget(int index) { + return _widgets.get(index); + } +} + +/// List +class ListWidget : WidgetGroup, OnScrollHandler { + protected Orientation _orientation = Orientation.Vertical; + /// returns linear layout orientation (Vertical, Horizontal) + @property Orientation orientation() { return _orientation; } + /// sets linear layout orientation + @property ListWidget orientation(Orientation value) { + _orientation = value; + _scrollbar.orientation = value; + requestLayout(); + return this; + } + + protected Rect[] _itemRects; + protected Point[] _itemSizes; + protected bool _needScrollbar; + protected Point _sbsz; // scrollbar size + protected ScrollBar _scrollbar; + protected int _lastMeasureWidth; + protected int _lastMeasureHeight; + + /// first visible item index + protected int _firstVisibleItem; + /// scroll position - offset of scroll area + protected int _scrollPosition; + /// maximum scroll position + protected int _maxScrollPosition; + /// client area rectangle (counting padding, margins, and scrollbar) + protected Rect _clientRc; + /// total height of all items for Vertical orientation, or width for Horizontal + protected int _totalSize; + + /// returns rectangle for item (not scrolled, first item starts at 0,0) + Rect itemRectNoScroll(int index) { + Rect res; + res = _itemRects[index]; + return res; + } + + /// returns rectangle for item (scrolled) + Rect itemRect(int index) { + Rect res = itemRectNoScroll(index); + if (_orientation == Orientation.Horizontal) { + res.left += _scrollPosition; + res.right += _scrollPosition; + } else { + res.top += _scrollPosition; + res.bottom += _scrollPosition; + } + return res; + } + + /// returns item index by 0-based offset from top/left of list content + int itemByPosition(int pos) { + return 0; + } + + protected ListAdapter _adapter; + /// when true, need to destroy adapter on list destroy + protected bool _ownAdapter; + + /// get adapter + @property ListAdapter adapter() { return _adapter; } + /// set adapter + @property ListWidget adapter(ListAdapter adapter) { + if (_adapter !is null && _ownAdapter) + destroy(_adapter); + _adapter = adapter; + _ownAdapter = false; + onAdapterChanged(); + return this; + } + /// set adapter + @property ListWidget ownAdapter(ListAdapter adapter) { + if (_adapter !is null && _ownAdapter) + destroy(_adapter); + _adapter = adapter; + _ownAdapter = true; + onAdapterChanged(); + return this; + } + + /// returns number of widgets in list + @property int itemCount() { + if (_adapter !is null) + return _adapter.itemCount; + return 0; + } + + /// return list item widget by item index + Widget itemWidget(int index) { + if (_adapter !is null) + return _adapter.itemWidget(index); + return null; + } + + void onAdapterChanged() { + requestLayout(); + } + + this(string ID = null, Orientation orientation = Orientation.Vertical) { + super(ID); + _orientation = orientation; + _scrollbar = new ScrollBar("listscroll", orientation); + _scrollbar.visibility = Visibility.Gone; + _scrollbar.onScrollEventListener = &onScrollEvent; + addChild(_scrollbar); + } + + ~this() { + if (_adapter !is null && _ownAdapter) + destroy(_adapter); + _adapter = null; + } + /// handle scroll event - override bool onScrollEvent(AbstractSlider source, ScrollEvent event) { - int newPosition = _scrollPosition; - if (event.action == ScrollAction.SliderMoved) { - // scroll - newPosition = event.position; - } else { - // use default handler for page/line up/down events - newPosition = event.defaultUpdatePosition(); - } - if (_scrollPosition != newPosition) { - _scrollPosition = newPosition; - if (_scrollPosition > _maxScrollPosition) - _scrollPosition = _maxScrollPosition; - if (_scrollPosition < 0) - _scrollPosition = 0; - invalidate(); - } - return true; - } - - /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). - override void measure(int parentWidth, int parentHeight) { + override bool onScrollEvent(AbstractSlider source, ScrollEvent event) { + int newPosition = _scrollPosition; + if (event.action == ScrollAction.SliderMoved) { + // scroll + newPosition = event.position; + } else { + // use default handler for page/line up/down events + newPosition = event.defaultUpdatePosition(); + } + if (_scrollPosition != newPosition) { + _scrollPosition = newPosition; + if (_scrollPosition > _maxScrollPosition) + _scrollPosition = _maxScrollPosition; + if (_scrollPosition < 0) + _scrollPosition = 0; + invalidate(); + } + return true; + } + + /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). + override void measure(int parentWidth, int parentHeight) { if (visibility == Visibility.Gone) { _measuredWidth = _measuredHeight = 0; return; } - if (_itemSizes.length < itemCount) - _itemSizes.length = itemCount; - 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; - _scrollbar.visibility = Visibility.Visible; - _scrollbar.measure(pwidth, pheight); - - _lastMeasureWidth = pwidth; - _lastMeasureHeight = pheight; - - int sbsize = _orientation == Orientation.Vertical ? _scrollbar.measuredWidth : _scrollbar.measuredHeight; - // measure children - Point sz; - _sbsz.clear; - for (int i = 0; i < itemCount; i++) { - Widget w = itemWidget(i); - if (w is null || w.visibility == Visibility.Gone) { - _itemSizes[i].x = _itemSizes[i].y = 0; - continue; - } - w.measure(pwidth, pheight); - _itemSizes[i].x = w.measuredWidth; - _itemSizes[i].y = w.measuredHeight; - if (_orientation == Orientation.Vertical) { - // Vertical - if (sz.x < w.measuredWidth) - sz.x = w.measuredWidth; - sz.y += w.measuredHeight; - } else { - // Horizontal - w.measure(pwidth, pheight); - if (sz.y < w.measuredHeight) - sz.y = w.measuredHeight; - sz.x += w.measuredWidth; - } - } - if (_orientation == Orientation.Vertical) { - if (pheight != SIZE_UNSPECIFIED && sz.y > pheight) { - // need scrollbar - if (pwidth != SIZE_UNSPECIFIED) { - pwidth -= sbsize; - _sbsz.x = sbsize; - _needScrollbar = true; - } - } - } else { - if (pwidth != SIZE_UNSPECIFIED && sz.x > pwidth) { - // need scrollbar - if (pheight != SIZE_UNSPECIFIED) { - pheight -= sbsize; - _sbsz.y = sbsize; - _needScrollbar = true; - } - } - } - if (_needScrollbar) { - // recalculate with scrollbar - sz.x = sz.y = 0; - for (int i = 0; i < itemCount; i++) { - Widget w = itemWidget(i); - if (w is null || w.visibility == Visibility.Gone) - continue; - w.measure(pwidth, pheight); - _itemSizes[i].x = w.measuredWidth; - _itemSizes[i].y = w.measuredHeight; - if (_orientation == Orientation.Vertical) { - // Vertical - if (sz.x < w.measuredWidth) - sz.x = w.measuredWidth; - sz.y += w.measuredHeight; - } else { - // Horizontal - w.measure(pwidth, pheight); - if (sz.y < w.measuredHeight) - sz.y = w.measuredHeight; - sz.x += w.measuredWidth; - } - } - } - measuredContent(parentWidth, parentHeight, sz.x + _sbsz.x, sz.y + _sbsz.y); - } - - - protected void updateItemPositions() { - Rect r; - int p = 0; - for (int i = 0; i < itemCount; i++) { - if (_itemSizes[i].x == 0 && _itemSizes[i].y == 0) - continue; - if (_orientation == Orientation.Vertical) { - // Vertical - int w = _clientRc.width; - int h = _itemSizes[i].y; - r.top = p; - r.bottom = p + h; - r.left = 0; - r.right = w; - _itemRects[i] = r; - p += h; - } else { - // Horizontal - int h = _clientRc.height; - int w = _itemSizes[i].x; - r.top = 0; - r.bottom = h; - r.left = p; - r.right = p + w; - _itemRects[i] = r; - p += w; - } - } - _totalSize = p; - if (_needScrollbar) { - if (_orientation == Orientation.Vertical) { - _scrollbar.setRange(0, p); - _scrollbar.pageSize = _clientRc.height; - _scrollbar.position = _scrollPosition; - } else { - _scrollbar.setRange(0, p); - _scrollbar.pageSize = _clientRc.width; - _scrollbar.position = _scrollPosition; - } - } - /// maximum scroll position - if (_orientation == Orientation.Vertical) { - _maxScrollPosition = _totalSize - _clientRc.height; - if (_maxScrollPosition < 0) - _maxScrollPosition = 0; - } else { - _maxScrollPosition = _totalSize - _clientRc.width; - if (_maxScrollPosition < 0) - _maxScrollPosition = 0; - } - if (_scrollPosition > _maxScrollPosition) - _scrollPosition = _maxScrollPosition; - if (_scrollPosition < 0) - _scrollPosition = 0; - if (_needScrollbar) { - if (_orientation == Orientation.Vertical) { - _scrollbar.position = _scrollPosition; - } else { - _scrollbar.position = _scrollPosition; - } - } - } - - /// 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) { - return; - } - _pos = rc; - - applyMargins(rc); - applyPadding(rc); - - if (_itemRects.length < itemCount) - _itemRects.length = itemCount; - - // measure again if client size has been changed - if (_lastMeasureWidth != rc.width || _lastMeasureHeight != rc.height) - measure(rc.width, rc.height); - - // layout scrollbar - if (_needScrollbar) { - _scrollbar.visibility = Visibility.Visible; - Rect sbrect = rc; - if (_orientation == Orientation.Vertical) - sbrect.left = sbrect.right - _sbsz.x; - else - sbrect.top = sbrect.bottom - _sbsz.y; - _scrollbar.layout(sbrect); - rc.right -= _sbsz.x; - rc.bottom -= _sbsz.y; - } else { - _scrollbar.visibility = Visibility.Gone; - } - - _clientRc = rc; - - // calc item rectangles - updateItemPositions(); - - _needLayout = false; - } - - /// 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); - auto saver = ClipRectSaver(buf, rc); - // draw scrollbar - if (_needScrollbar) - _scrollbar.onDraw(buf); - - Point scrollOffset; - if (_orientation == Orientation.Vertical) { - scrollOffset.y = _scrollPosition; - } else { - scrollOffset.x = _scrollPosition; - } - // todo: scrollOffset - // draw items - for (int i = 0; i < itemCount; i++) { - Rect itemrc = _itemRects[i]; - itemrc.left += rc.left - scrollOffset.x; - itemrc.right += rc.left - scrollOffset.x; - itemrc.top += rc.top - scrollOffset.y; - itemrc.bottom += rc.top - scrollOffset.y; - if (itemrc.intersects(rc)) { - Widget w = itemWidget(i); - if (w is null || w.visibility != Visibility.Visible) - continue; - w.measure(itemrc.width, itemrc.height); - w.layout(itemrc); - w.onDraw(buf); - } - } - } - -} - + if (_itemSizes.length < itemCount) + _itemSizes.length = itemCount; + 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; + _scrollbar.visibility = Visibility.Visible; + _scrollbar.measure(pwidth, pheight); + + _lastMeasureWidth = pwidth; + _lastMeasureHeight = pheight; + + int sbsize = _orientation == Orientation.Vertical ? _scrollbar.measuredWidth : _scrollbar.measuredHeight; + // measure children + Point sz; + _sbsz.clear; + for (int i = 0; i < itemCount; i++) { + Widget w = itemWidget(i); + if (w is null || w.visibility == Visibility.Gone) { + _itemSizes[i].x = _itemSizes[i].y = 0; + continue; + } + w.measure(pwidth, pheight); + _itemSizes[i].x = w.measuredWidth; + _itemSizes[i].y = w.measuredHeight; + if (_orientation == Orientation.Vertical) { + // Vertical + if (sz.x < w.measuredWidth) + sz.x = w.measuredWidth; + sz.y += w.measuredHeight; + } else { + // Horizontal + w.measure(pwidth, pheight); + if (sz.y < w.measuredHeight) + sz.y = w.measuredHeight; + sz.x += w.measuredWidth; + } + } + if (_orientation == Orientation.Vertical) { + if (pheight != SIZE_UNSPECIFIED && sz.y > pheight) { + // need scrollbar + if (pwidth != SIZE_UNSPECIFIED) { + pwidth -= sbsize; + _sbsz.x = sbsize; + _needScrollbar = true; + } + } + } else { + if (pwidth != SIZE_UNSPECIFIED && sz.x > pwidth) { + // need scrollbar + if (pheight != SIZE_UNSPECIFIED) { + pheight -= sbsize; + _sbsz.y = sbsize; + _needScrollbar = true; + } + } + } + if (_needScrollbar) { + // recalculate with scrollbar + sz.x = sz.y = 0; + for (int i = 0; i < itemCount; i++) { + Widget w = itemWidget(i); + if (w is null || w.visibility == Visibility.Gone) + continue; + w.measure(pwidth, pheight); + _itemSizes[i].x = w.measuredWidth; + _itemSizes[i].y = w.measuredHeight; + if (_orientation == Orientation.Vertical) { + // Vertical + if (sz.x < w.measuredWidth) + sz.x = w.measuredWidth; + sz.y += w.measuredHeight; + } else { + // Horizontal + w.measure(pwidth, pheight); + if (sz.y < w.measuredHeight) + sz.y = w.measuredHeight; + sz.x += w.measuredWidth; + } + } + } + measuredContent(parentWidth, parentHeight, sz.x + _sbsz.x, sz.y + _sbsz.y); + } + + + protected void updateItemPositions() { + Rect r; + int p = 0; + for (int i = 0; i < itemCount; i++) { + if (_itemSizes[i].x == 0 && _itemSizes[i].y == 0) + continue; + if (_orientation == Orientation.Vertical) { + // Vertical + int w = _clientRc.width; + int h = _itemSizes[i].y; + r.top = p; + r.bottom = p + h; + r.left = 0; + r.right = w; + _itemRects[i] = r; + p += h; + } else { + // Horizontal + int h = _clientRc.height; + int w = _itemSizes[i].x; + r.top = 0; + r.bottom = h; + r.left = p; + r.right = p + w; + _itemRects[i] = r; + p += w; + } + } + _totalSize = p; + if (_needScrollbar) { + if (_orientation == Orientation.Vertical) { + _scrollbar.setRange(0, p); + _scrollbar.pageSize = _clientRc.height; + _scrollbar.position = _scrollPosition; + } else { + _scrollbar.setRange(0, p); + _scrollbar.pageSize = _clientRc.width; + _scrollbar.position = _scrollPosition; + } + } + /// maximum scroll position + if (_orientation == Orientation.Vertical) { + _maxScrollPosition = _totalSize - _clientRc.height; + if (_maxScrollPosition < 0) + _maxScrollPosition = 0; + } else { + _maxScrollPosition = _totalSize - _clientRc.width; + if (_maxScrollPosition < 0) + _maxScrollPosition = 0; + } + if (_scrollPosition > _maxScrollPosition) + _scrollPosition = _maxScrollPosition; + if (_scrollPosition < 0) + _scrollPosition = 0; + if (_needScrollbar) { + if (_orientation == Orientation.Vertical) { + _scrollbar.position = _scrollPosition; + } else { + _scrollbar.position = _scrollPosition; + } + } + } + + /// 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); + + if (_itemRects.length < itemCount) + _itemRects.length = itemCount; + + // measure again if client size has been changed + if (_lastMeasureWidth != rc.width || _lastMeasureHeight != rc.height) + measure(rc.width, rc.height); + + // layout scrollbar + if (_needScrollbar) { + _scrollbar.visibility = Visibility.Visible; + Rect sbrect = rc; + if (_orientation == Orientation.Vertical) + sbrect.left = sbrect.right - _sbsz.x; + else + sbrect.top = sbrect.bottom - _sbsz.y; + _scrollbar.layout(sbrect); + rc.right -= _sbsz.x; + rc.bottom -= _sbsz.y; + } else { + _scrollbar.visibility = Visibility.Gone; + } + + _clientRc = rc; + + // calc item rectangles + updateItemPositions(); + } + + /// 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); + auto saver = ClipRectSaver(buf, rc); + // draw scrollbar + if (_needScrollbar) + _scrollbar.onDraw(buf); + + Point scrollOffset; + if (_orientation == Orientation.Vertical) { + scrollOffset.y = _scrollPosition; + } else { + scrollOffset.x = _scrollPosition; + } + // todo: scrollOffset + // draw items + for (int i = 0; i < itemCount; i++) { + Rect itemrc = _itemRects[i]; + itemrc.left += rc.left - scrollOffset.x; + itemrc.right += rc.left - scrollOffset.x; + itemrc.top += rc.top - scrollOffset.y; + itemrc.bottom += rc.top - scrollOffset.y; + if (itemrc.intersects(rc)) { + Widget w = itemWidget(i); + if (w is null || w.visibility != Visibility.Visible) + continue; + w.measure(itemrc.width, itemrc.height); + w.layout(itemrc); + w.onDraw(buf); + } + } + } + +} + diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index d8054c1d..d9cedfc9 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -1,12 +1,12 @@ -module dlangui.widgets.tabs; - -import dlangui.widgets.layouts; -import dlangui.widgets.controls; - -interface TabHandler { - void onTabChanged(string newActiveTabId, string previousTabId); -} - +module dlangui.widgets.tabs; + +import dlangui.widgets.layouts; +import dlangui.widgets.controls; + +interface TabHandler { + void onTabChanged(string newActiveTabId, string previousTabId); +} + class TabItem { private string _iconRes; private string _id; @@ -229,109 +229,109 @@ class TabControl : WidgetGroup { } 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); - auto saver = 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); - } - } - - protected string _selectedTabId; - - void selectTab(int index) { - if (_children.get(index + 1).compareId(_selectedTabId)) - return; // already selected - string previousSelectedTab = _selectedTabId; - for (int i = 1; i < _children.count; i++) { - if (index == i - 1) { - _children.get(i).state = State.Selected; - _selectedTabId = _children.get(i).id; - } else { - _children.get(i).state = State.Normal; - } - } - if (_onTabChanged !is null) - _onTabChanged(_selectedTabId, previousSelectedTab); - } + /// 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); + auto saver = 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); + } + } + + protected string _selectedTabId; + + void selectTab(int index) { + if (_children.get(index + 1).compareId(_selectedTabId)) + return; // already selected + string previousSelectedTab = _selectedTabId; + for (int i = 1; i < _children.count; i++) { + if (index == i - 1) { + _children.get(i).state = State.Selected; + _selectedTabId = _children.get(i).id; + } else { + _children.get(i).state = State.Normal; + } + } + if (_onTabChanged !is null) + _onTabChanged(_selectedTabId, previousSelectedTab); + } } /// container for widgets controlled by TabControl @@ -396,12 +396,25 @@ class TabHost : FrameLayout, TabHandler { return this; } /// select tab - void selectTab(string ID) { - int index = _tabControl.tabIndex(ID); - if (index != -1) { - _tabControl.selectTab(index); - } - } + void selectTab(string ID) { + int index = _tabControl.tabIndex(ID); + if (index != -1) { + _tabControl.selectTab(index); + } + } +// /// request relayout of widget and its children +// override void requestLayout() { +// Log.d("TabHost.requestLayout called"); +// super.requestLayout(); +// //_needLayout = true; +// } +// /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). +// override void layout(Rect rc) { +// Log.d("TabHost.layout() called"); +// super.layout(rc); +// Log.d("after layout(): needLayout = ", needLayout); +// } + } /// compound widget - contains from TabControl widget (tabs header) and TabHost (content pages) @@ -444,7 +457,13 @@ class TabWidget : VerticalLayout, TabHandler { return this; } /// select tab - void selectTab(string ID) { - _tabHost.selectTab(ID); - } + void selectTab(string ID) { + _tabHost.selectTab(ID); + } +// /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). +// override void layout(Rect rc) { +// Log.d("TabWidget.layout() called"); +// super.layout(rc); +// Log.d("after layout(): tabhost.needLayout = ", _tabHost.needLayout); +// } }