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);
+// }
}