From c73f1ae0b0899daf0d66ea9b9d9961bff9b237de Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Mon, 17 Mar 2014 17:56:52 +0400 Subject: [PATCH] horizontal layout; mouse event processing fixes --- examples/example1/main.d | 13 ++ src/dlangui/platforms/windows/winapp.d | 28 ++- src/dlangui/widgets/layouts.d | 286 +++++++++++++++---------- 3 files changed, 207 insertions(+), 120 deletions(-) diff --git a/examples/example1/main.d b/examples/example1/main.d index 18de0608..871de350 100644 --- a/examples/example1/main.d +++ b/examples/example1/main.d @@ -44,6 +44,18 @@ extern (C) int UIAppMain(string[] args) { layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0")); layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget")); layout.addChild((new Button()).text("Button1")); //.textColor(0x40FF4000) + + LinearLayout hlayout = new HorizontalLayout(); + hlayout.addChild((new Button()).text("<<")); //.textColor(0x40FF4000) + hlayout.addChild((new TextWidget()).text("Several")); + hlayout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5))); + hlayout.addChild((new TextWidget()).text("items")); + hlayout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5))); + hlayout.addChild((new TextWidget()).text("in horizontal layout")); + hlayout.addChild((new Button()).text(">>")); //.textColor(0x40FF4000) + hlayout.backgroundColor = 0x8080C0; + layout.addChild(hlayout); + layout.addChild((new Button()).textColor(0x000000FF).text("Button2")); layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget")); layout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5))); @@ -51,6 +63,7 @@ extern (C) int UIAppMain(string[] args) { layout.addChild((new Button()).textColor(0x000000FF).text("Button3").layoutHeight(FILL_PARENT)); layout.addChild((new TextWidget()).textColor(0x004000).text("Text widget3 with very long text")); + layout.layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT); window.mainWidget = layout; diff --git a/src/dlangui/platforms/windows/winapp.d b/src/dlangui/platforms/windows/winapp.d index 18d221a0..495c8342 100644 --- a/src/dlangui/platforms/windows/winapp.d +++ b/src/dlangui/platforms/windows/winapp.d @@ -362,11 +362,9 @@ class Win32Window : Window { pbuttonDetails = &_mbutton; break; case WM_MOUSELEAVE: + Log.d("WM_MOUSELEAVE"); action = MouseAction.Leave; - if (_mouseTracking) { - _mouseTracking = false; - ReleaseCapture(); - } + break; case WM_MOUSEWHEEL: { action = MouseAction.Wheel; @@ -388,9 +386,18 @@ class Win32Window : Window { } else if (action == MouseAction.ButtonDown) { pbuttonDetails.up(x, y, cast(ushort)flags); } + if (((message == WM_MOUSELEAVE) || (x < 0 || y < 0 || x > _dx || y > _dy)) && _mouseTracking) { + action = MouseAction.Leave; + Log.d("WM_MOUSELEAVE - releasing capture"); + _mouseTracking = false; + ReleaseCapture(); + } if (message != WM_MOUSELEAVE && !_mouseTracking) { - _mouseTracking = true; - SetCapture(_hwnd); + if (x >=0 && y >= 0 && x < _dx && y < _dy) { + Log.d("Setting capture"); + _mouseTracking = true; + SetCapture(_hwnd); + } } MouseEvent event = new MouseEvent(action, button, cast(ushort)flags, x, y, wheelDelta); event.lbutton = _lbutton; @@ -679,9 +686,12 @@ LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_MOUSEWHEEL: - if (window !is null) - window.onMouse(message, cast(ushort)wParam, cast(short)(lParam & 0xFFFF), cast(short)((lParam >> 16) & 0xFFFF)); - return 0; // processed + if (window !is null) { + if (window.onMouse(message, cast(ushort)wParam, cast(short)(lParam & 0xFFFF), cast(short)((lParam >> 16) & 0xFFFF))) + return 0; // processed + } + // not processed - default handling + return DefWindowProc(hwnd, message, wParam, lParam); case WM_GETMINMAXINFO: case WM_NCCREATE: case WM_NCCALCSIZE: diff --git a/src/dlangui/widgets/layouts.d b/src/dlangui/widgets/layouts.d index 8177e288..a7ff553d 100644 --- a/src/dlangui/widgets/layouts.d +++ b/src/dlangui/widgets/layouts.d @@ -12,21 +12,33 @@ 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 _layoutSize; // layout size for primary dimension - int _secondarySize; // other measured size + 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; } - /// set item and measure it - void measure(Widget widget, Orientation orientation, int parentWidth, int parentHeight) { + /// 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); - if (orientation == Orientation.Horizontal) { + _weight = _widget.layoutWeight; + if (_orientation == Orientation.Horizontal) { _secondarySize = _widget.measuredHeight; _measuredSize = _widget.measuredWidth; _minSize = _widget.minWidth; @@ -41,6 +53,9 @@ struct LayoutItem { } _fillParent = _layoutSize == FILL_PARENT; } + void layout(ref Rect rc) { + _widget.layout(rc); + } } /// helper class for layouts @@ -50,31 +65,156 @@ class LayoutItems { 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(Orientation orientation, ref WidgetList widgets, int parentWidth, int parentHeight) { + 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(); - _orientation = orientation; // reserve space if (_list.length < widgets.count) _list.length = widgets.count; - _totalSize = 0; - _maxSecondarySize = 0; - // copy and measure + // copy for (int i = 0; i < widgets.count; i++) { Widget item = widgets.get(i); if (item.visibility == Visibility.Gone) continue; - _list[_count].measure(item, orientation, parentWidth, parentHeight); - if (_maxSecondarySize < _list[_count]._secondarySize) - _maxSecondarySize = _list[_count]._secondarySize; - _totalSize = _list[_count]._measuredSize; - _count++; + _list[_count++].set(item, _orientation); } - return _orientation == Orientation.Horizontal ? Point(_totalSize, _maxSecondarySize) : Point(_maxSecondarySize, _totalSize); } + 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(); @@ -110,7 +250,9 @@ class LinearLayout : WidgetGroup { if (parentHeight != SIZE_UNSPECIFIED) pheight -= m.top + m.bottom + p.top + p.bottom; // measure children - Point sz = _layoutItems.measure(orientation, _children, pwidth, pheight); + _layoutItems.setLayoutParams(orientation, layoutWidth, layoutHeight); + _layoutItems.setWidgets(_children); + Point sz = _layoutItems.measure(pwidth, pheight); measuredContent(parentWidth, parentHeight, sz.x, sz.y); } @@ -122,100 +264,7 @@ class LinearLayout : WidgetGroup { _pos = rc; applyMargins(rc); applyPadding(rc); - int contentWidth = 0; - int contentHeight = 0; - if (orientation == Orientation.Vertical) { - // Vertical - 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 = 0; - for (int i = 0; i < _children.count; i++) { - Widget item = _children.get(i); - if (item.visibility == Visibility.Gone) - continue; - visibleCount++; - int weight = item.layoutWeight; - int size = item.measuredHeight; - totalSize += size; - if (maxItem < item.measuredWidth) - maxItem = item.measuredWidth; - if (item.layoutHeight == FILL_PARENT) { - resizableWeight += weight; - resizableSize += size * weight; - } else { - nonresizableWeight += weight; - nonresizableSize += size * weight; - } - } - if (layoutWidth == WRAP_CONTENT && maxItem < rc.width) - contentWidth = maxItem; - else - contentWidth = rc.width; - if (layoutHeight == FILL_PARENT || totalSize > rc.height) - delta = rc.height - 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 - Widget lastResized = null; - for (int i = 0; i < _children.count; i++) { - Widget item = _children.get(i); - if (item.visibility == Visibility.Gone) - continue; - if (item.layoutHeight == FILL_PARENT || needForceResize) { - lastResized = item; - } - } - // final resize and layout of children - int position = 0; - int deltaTotal = 0; - for (int i = 0; i < _children.count; i++) { - Widget item = _children.get(i); - if (item.visibility == Visibility.Gone) - continue; - int layoutSize = item.layoutHeight; - int weight = item.layoutWeight; - int size = item.measuredHeight; - 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 (item == lastResized) { - correction += delta - deltaTotal; - } - size += correction; - } - // apply size - Rect childRect = rc; - childRect.top += position; - childRect.bottom = childRect.top + size; - childRect.right = childRect.left + contentWidth; - item.layout(childRect); - position += size; - } - } else { - // Horizontal - } + _layoutItems.layout(rc); _needLayout = false; } /// Draw widget at its position to buffer @@ -236,3 +285,18 @@ class LinearLayout : WidgetGroup { } } + +class VerticalLayout : LinearLayout { + this(string ID = null) { + super(ID); + orientation = Orientation.Vertical; + } +} + +class HorizontalLayout : LinearLayout { + this(string ID = null) { + super(ID); + orientation = Orientation.Horizontal; + } +} +