diff --git a/examples/example1/src/example1.d b/examples/example1/src/example1.d index 742e724d..fb7bed77 100644 --- a/examples/example1/src/example1.d +++ b/examples/example1/src/example1.d @@ -267,7 +267,11 @@ extern (C) int UIAppMain(string[] args) { // create window Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable, 800, 700); - + // here you can see window or content resize mode + //Window window = Platform.instance.createWindow("DlangUI Example 1", null, WindowFlag.Resizable, 400, 400); + //window.windowOrContentResizeMode = WindowOrContentResizeMode.resizeWindow; + //window.windowOrContentResizeMode = WindowOrContentResizeMode.scrollWindow; + //window.windowOrContentResizeMode = WindowOrContentResizeMode.shrinkWidgets; static if (true) { VerticalLayout contentLayout = new VerticalLayout(); @@ -458,6 +462,7 @@ extern (C) int UIAppMain(string[] args) { // most of controls example { LinearLayout controls = new VerticalLayout("controls"); + controls.layoutHeight(FILL_PARENT); controls.padding = Rect(12.pointsToPixels,12.pointsToPixels,12.pointsToPixels,12.pointsToPixels); HorizontalLayout line1 = new HorizontalLayout(); @@ -548,9 +553,12 @@ extern (C) int UIAppMain(string[] args) { HorizontalLayout line4 = new HorizontalLayout(); line4.layoutWidth(FILL_PARENT); + line4.layoutHeight(FILL_PARENT); GroupBox gbgrid = new GroupBox("grid", "StringGridWidget"d, Orientation.Horizontal); StringGridWidget grid = new StringGridWidget("stringgrid"); grid.resize(12, 10); + gbgrid.layoutWidth(FILL_PARENT); + gbgrid.layoutHeight(FILL_PARENT); grid.layoutWidth(FILL_PARENT); grid.layoutHeight(FILL_PARENT); foreach (index, month; ["January"d, "February"d, "March"d, "April"d, "May"d, "June"d, "July"d, "August"d, "September"d, "October"d, "November"d, "December"d]) diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 20cc9546..b1a90cb6 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -24,6 +24,7 @@ public import dlangui.core.events; import dlangui.core.collections; import dlangui.widgets.widget; import dlangui.widgets.popup; +import dlangui.widgets.scrollbar; import dlangui.graphics.drawbuf; import dlangui.core.stdaction; import dlangui.core.asyncsocket; @@ -91,6 +92,16 @@ enum DialogDisplayMode : ulong { allTypesOfDialogsInPopup = fileDialogInPopup | messageBoxInPopup | inputBoxInPopup | settingsDialogInPopup | userDialogInPopup } +/// Sets what's should be done when window content is too big +enum WindowOrContentResizeMode { + /// widgets are shrink to fit in window + shrinkWidgets, + /// resize window when window is too small + resizeWindow, + /// add scrollbars to window + scrollWindow +} + /// Window state signal listener interface OnWindowStateHandler { /// signal listener - called when state of window is changed @@ -213,7 +224,16 @@ class Window : CustomEventTarget { protected Widget _mainWidget; protected EventList _eventList; protected uint _flags; - + /// minimal good looking content width + protected int _minContentWidth; + /// minimal good looking content height + protected int _minContentHeight; + + // current content width calculated using _windowOrContentResizeMode flag, usually used in measure() + protected int _currentContentWidth; + // current content height calculated using _windowOrContentResizeMode flag, usually used in measure() + protected int _currentContentHeight; + @property uint flags() { return _flags; } @property uint backgroundColor() const { return _backgroundColor; } @property void backgroundColor(uint color) { _backgroundColor = color; } @@ -241,7 +261,21 @@ class Window : CustomEventTarget { @property void caretReplace(bool flg) { _caretReplace = flg; } @property bool caretReplace() { return _caretReplace; } - // Abstract methods : override in platform implementatino + // window content resize mode + //protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.resizeWindow; + //protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.shrinkWidgets; + protected WindowOrContentResizeMode _windowOrContentResizeMode = WindowOrContentResizeMode.scrollWindow; + + @property WindowOrContentResizeMode windowOrContentResizeMode() {return _windowOrContentResizeMode; } + @property void windowOrContentResizeMode(WindowOrContentResizeMode newMode) { + _windowOrContentResizeMode = newMode; + if (_mainWidget) { + _mainWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED); + adjustWindowOrContentSize(_mainWidget.measuredWidth, _mainWidget.measuredHeight); + } + } + + // Abstract methods : override in platform implementation /// show window abstract void show(); @@ -304,26 +338,168 @@ class Window : CustomEventTarget { /// set window rectangle bool moveAndResizeWindow(Rect rc, bool activate = false) { return setWindowState(WindowState.unspecified, activate, rc); } + // things needed for WindowOrContentResizeMode.scrollWindow: + /// vertical scrollbar control + protected ScrollBar _vScrollBar = null; + /// horizontal scrollbar control + protected ScrollBar _hScrollBar = null; + + /// Sets the minimal content size and adjust window or content should be called from window show + void adjustWindowOrContentSize(int minContentWidth, int minContentHeight) { + _minContentWidth = minContentWidth; + _minContentHeight = minContentHeight; + if (_windowOrContentResizeMode == WindowOrContentResizeMode.resizeWindow) + resizeWindow(Point(minContentWidth, minContentHeight)); + updateWindowOrContentSize(); + } + + /// update current content size based on windowOrContentResizeMode flag, usually used when window is resized + void updateWindowOrContentSize() { + final switch (_windowOrContentResizeMode) { + case WindowOrContentResizeMode.shrinkWidgets: { + _currentContentWidth = _windowRect.right; + _currentContentHeight = _windowRect.bottom; + return; + } + case WindowOrContentResizeMode.resizeWindow: { + //maybe should not permit to resize when new window size is smaller than content size, now do nothing + _currentContentWidth = _windowRect.right; + _currentContentHeight = _windowRect.bottom; + break; + } + case WindowOrContentResizeMode.scrollWindow: { + if (_windowRect.right < _minContentWidth) { + // create scrollbar + _currentContentWidth = _minContentWidth; + if (!_hScrollBar) { + _hScrollBar = new ScrollBar(null,Orientation.Horizontal); + _hScrollBar.scrollEvent = delegate bool (AbstractSlider source, ScrollEvent event) { + if (event.action == ScrollAction.SliderMoved || event.action == ScrollAction.SliderReleased) + requestLayout(); + else if (event.action == ScrollAction.PageUp) { + source.position = max(0, source.position - _windowRect.right * 3 / 4); + requestLayout(); + } else if (event.action == ScrollAction.PageDown) { + source.position = min(source.maxValue - _windowRect.right, source.position + _windowRect.right *3 / 4); + requestLayout(); + } else if (event.action == ScrollAction.LineUp) { + source.position = max(0, source.position - _windowRect.right / 10); + requestLayout(); + } else if (event.action == ScrollAction.LineDown) { + source.position = min(source.maxValue - _windowRect.right, source.position + _windowRect.right / 10); + requestLayout(); + } + return true; + }; + } + _hScrollBar.measure(_windowRect.right, _windowRect.bottom); + if (windowRect().bottom < _minContentHeight) + _hScrollBar.setRange(0, _minContentWidth + _hScrollBar.measuredHeight); + else + _hScrollBar.setRange(0, _minContentWidth); + _hScrollBar.pageSize(_windowRect.right); + _hScrollBar.position(0); + } + else { + if (_hScrollBar) { + destroy(_hScrollBar); + _hScrollBar = null; + } + _currentContentWidth = _windowRect.right; + } + + if (windowRect().bottom < _minContentHeight) { + // create scrollbar + _currentContentHeight = _minContentHeight; + if (!_vScrollBar) { + _vScrollBar = new ScrollBar(null,Orientation.Vertical); + _vScrollBar.scrollEvent = delegate bool (AbstractSlider source, ScrollEvent event) { + if (event.action == ScrollAction.SliderMoved || event.action == ScrollAction.SliderReleased) + requestLayout(); + else if (event.action == ScrollAction.PageUp) { + source.position = max(0, source.position - _windowRect.bottom * 3 / 4); + requestLayout(); + } else if (event.action == ScrollAction.PageDown) { + source.position = min(source.maxValue - _windowRect.bottom, source.position + _windowRect.bottom *3 / 4); + requestLayout(); + } else if (event.action == ScrollAction.LineUp) { + source.position = max(0, source.position - _windowRect.bottom / 10); + requestLayout(); + } else if (event.action == ScrollAction.LineDown) { + source.position = min(source.maxValue - _windowRect.bottom, source.position + _windowRect.bottom / 10); + requestLayout(); + } + return true; + }; + } + _vScrollBar.measure(_windowRect.right, _windowRect.bottom); + if (_hScrollBar) + _vScrollBar.setRange(0, _minContentHeight+_hScrollBar.measuredHeight); + else + _vScrollBar.setRange(0, _minContentHeight); + _vScrollBar.pageSize(windowRect().bottom); + _vScrollBar.position(0); + + if (!_hScrollBar) + _currentContentWidth = _windowRect.right - _vScrollBar.measuredWidth; + } + else { + if (_vScrollBar) { + destroy(_vScrollBar); + _vScrollBar = null; + } + _currentContentHeight = _hScrollBar ? _windowRect.bottom - _hScrollBar.measuredHeight : _windowRect.bottom; + } + return; + } + } + } + + /// requests layout for main widget and popups void requestLayout() { if (_mainWidget) _mainWidget.requestLayout(); + if (_hScrollBar) + _hScrollBar.requestLayout(); + if (_vScrollBar) + _vScrollBar.requestLayout(); foreach(p; _popups) p.requestLayout(); if (_tooltip.popup) _tooltip.popup.requestLayout(); } void measure() { + if (_hScrollBar) + _hScrollBar.measure(_dx, _dy); + + if (_vScrollBar) + _vScrollBar.measure(_dx, _dy); + if (_mainWidget !is null) { - _mainWidget.measure(_dx, _dy); + _mainWidget.measure(_currentContentWidth, _currentContentHeight); } foreach(p; _popups) - p.measure(_dx, _dy); + p.measure(_currentContentWidth, _currentContentHeight); if (_tooltip.popup) - _tooltip.popup.measure(_dx, _dy); + _tooltip.popup.measure(_currentContentWidth, _currentContentHeight); } void layout() { - Rect rc = Rect(0, 0, _dx, _dy); + if (_hScrollBar) + _hScrollBar.layout(Rect(0, _dy - _hScrollBar.measuredHeight, _vScrollBar ? _dx - _vScrollBar.measuredWidth : _dx, _dy)); + + if (_vScrollBar) + _vScrollBar.layout(Rect(_dx - _vScrollBar.measuredWidth, 0, _dx, _hScrollBar ? _dy - _hScrollBar.measuredHeight : _dy)); + + int deltaX = 0; + if (_hScrollBar) + deltaX = -_hScrollBar.position; + + int deltaY = 0; + if (_vScrollBar) + deltaY = -_vScrollBar.position; + + Rect rc = Rect(deltaX, deltaY, _currentContentWidth + deltaX, _currentContentHeight + deltaY); if (_mainWidget !is null) { _mainWidget.layout(rc); } @@ -337,9 +513,13 @@ class Window : CustomEventTarget { return; _dx = width; _dy = height; + // fix window rect for platforms that don't set it yet + _windowRect.right = width; + _windowRect.bottom = height; if (_mainWidget !is null) { Log.d("onResize ", _dx, "x", _dy); long measureStart = currentTimeMillis; + updateWindowOrContentSize(); measure(); //Log.d("measured size: ", _mainWidget.measuredWidth, "x", _mainWidget.measuredHeight); long measureEnd = currentTimeMillis; @@ -496,7 +676,7 @@ class Window : CustomEventTarget { return null; } - /// returns true if widget is child of either main widget or one of popups + /// returns true if widget is child of either main widget, one of popups or window scrollbar bool isChild(Widget w) { if (_mainWidget !is null && _mainWidget.isChild(w)) return true; @@ -506,6 +686,10 @@ class Window : CustomEventTarget { if (_tooltip.popup) if (_tooltip.popup.isChild(w)) return true; + if (_hScrollBar !is null && _hScrollBar.isChild(w)) + return true; + if (_vScrollBar !is null && _vScrollBar.isChild(w)) + return true; return false; } @@ -533,6 +717,14 @@ class Window : CustomEventTarget { destroy(_mainWidget); _mainWidget = null; } + if (_hScrollBar) { + destroy(_hScrollBar); + _hScrollBar = null; + } + if (_vScrollBar) { + destroy(_vScrollBar); + _vScrollBar = null; + } destroy(_eventList); destroy(_timerQueue); _eventList = null; @@ -627,6 +819,13 @@ class Window : CustomEventTarget { if (_tooltip.popup) _tooltip.popup.onDraw(buf); + if (_hScrollBar) + _hScrollBar.onDraw(buf); + if (_vScrollBar) + _vScrollBar.onDraw(buf); + if (_hScrollBar && _vScrollBar) + buf.fillRect(Rect(_vScrollBar.left, _hScrollBar.top, buf.width, buf.height), _backgroundColor); + long drawEnd = currentTimeMillis; debug(DebugRedraw) { if (drawEnd - drawStart > PERFORMANCE_LOGGING_THRESHOLD_MS) @@ -931,9 +1130,13 @@ class Window : CustomEventTarget { /// handle theme change: e.g. reload some themed resources void dispatchThemeChanged() { + if (_hScrollBar) + _hScrollBar.onThemeChanged(); + if (_vScrollBar) + _vScrollBar.onThemeChanged(); if (_mainWidget) _mainWidget.onThemeChanged(); - // draw popups + // draw popups foreach(p; _popups) { p.onThemeChanged(); } @@ -1128,10 +1331,23 @@ class Window : CustomEventTarget { return true; } } - if (!modal) - res = dispatchMouseEvent(_mainWidget, event, cursorIsSet); - else - res = dispatchMouseEvent(modal, event, cursorIsSet); + if (!modal) { + res = false; + if (_hScrollBar) + res = dispatchMouseEvent(_hScrollBar, event, cursorIsSet); + if (!res && _vScrollBar) + res = dispatchMouseEvent(_vScrollBar, event, cursorIsSet); + if (!res) + res = dispatchMouseEvent(_mainWidget, event, cursorIsSet); + } + else { + if (_hScrollBar) + res = dispatchMouseEvent(_hScrollBar, event, cursorIsSet); + if (!res && _vScrollBar) + res = dispatchMouseEvent(_vScrollBar, event, cursorIsSet); + if (!res) + res = dispatchMouseEvent(modal, event, cursorIsSet); + } } return res || processed || _mainWidget.needDraw; } @@ -1184,6 +1400,10 @@ class Window : CustomEventTarget { if (_mainWidget is null) return false; checkUpdateNeeded(_mainWidget, needDraw, needLayout, animationActive); + if (_hScrollBar) + checkUpdateNeeded(_hScrollBar, needDraw, needLayout, animationActive); + if (_vScrollBar) + checkUpdateNeeded(_vScrollBar, needDraw, needLayout, animationActive); foreach(p; _popups) checkUpdateNeeded(p, needDraw, needLayout, animationActive); if (_tooltip.popup) diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d index 31bc0086..824df97d 100644 --- a/src/dlangui/platforms/sdl/sdlapp.d +++ b/src/dlangui/platforms/sdl/sdlapp.d @@ -350,16 +350,15 @@ class SDLWindow : Window { Log.e("Window is shown without main widget"); _mainWidget = new Widget(); } - if (_mainWidget && !(_flags & WindowFlag.Resizable)) { + if (_mainWidget) { _mainWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED); - SDL_SetWindowSize(_win, _mainWidget.measuredWidth, _mainWidget.measuredHeight); + adjustWindowOrContentSize(_mainWidget.measuredWidth, _mainWidget.measuredHeight); } + SDL_ShowWindow(_win); if (_mainWidget) _mainWidget.setFocus(); fixSize(); - //update(true); - //redraw(); SDL_RaiseWindow(_win); invalidate(); } diff --git a/src/dlangui/platforms/windows/winapp.d b/src/dlangui/platforms/windows/winapp.d index a741db4f..2eda59c6 100644 --- a/src/dlangui/platforms/windows/winapp.d +++ b/src/dlangui/platforms/windows/winapp.d @@ -432,17 +432,13 @@ class Win32Window : Window { _mainWidget = new Widget(); } ReleaseCapture(); - if (!(_flags & WindowFlag.Resizable) && _mainWidget) { + + if (_mainWidget) { _mainWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED); - int dx = _mainWidget.measuredWidth; - int dy = _mainWidget.measuredHeight; - dx += 2 * GetSystemMetrics(SM_CXDLGFRAME); - dy += GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYDLGFRAME); - // TODO: ensure size less than screen size - SetWindowPos(_hwnd, HWND_TOP, 0, 0, dx, dy, SWP_NOMOVE| SWP_SHOWWINDOW); - } else { - ShowWindow(_hwnd, SW_SHOWNORMAL); + adjustWindowOrContentSize(_mainWidget.measuredWidth, _mainWidget.measuredHeight); } + + ShowWindow(_hwnd, SW_SHOWNORMAL); if (_mainWidget) _mainWidget.setFocus(); SetFocus(_hwnd); diff --git a/src/dlangui/platforms/x11/x11app.d b/src/dlangui/platforms/x11/x11app.d index 97112a06..a5c01e62 100644 --- a/src/dlangui/platforms/x11/x11app.d +++ b/src/dlangui/platforms/x11/x11app.d @@ -428,8 +428,13 @@ class X11Window : DWindow { Log.d("Open GL support is disabled"); } } - if (_mainWidget) - _mainWidget.setFocus(); + if (_mainWidget) { + _mainWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED); + _windowRect.right = _dx;// hack to set windowRect, remove when _windowRect will be full supported on X11 + _windowRect.bottom = _dy; + adjustWindowOrContentSize(_mainWidget.measuredWidth, _mainWidget.measuredHeight); + _mainWidget.setFocus(); + } } protected final void changeWindowState(int action, Atom firstProperty, Atom secondProperty = None) nothrow @@ -1537,6 +1542,13 @@ class X11Platform : Platform { w.requestLayout(); } } + + /// handle theme change: e.g. reload some themed resources + override void onThemeChanged() { + foreach(w; _windowMap) + w.dispatchThemeChanged(); + } + } import core.thread; diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 0c704a7a..482b2b51 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -1899,6 +1899,10 @@ class EditLine : EditWidgetBase { protected dstring _measuredText; protected int[] _measuredTextWidths; protected Point _measuredTextSize; + + protected Point _measuredTextToSetWidgetSize; + protected dstring _textToSetWidgetSize = "aaaaa"d; + protected int[] _measuredTextToSetWidgetSizeWidths; protected dchar _passwordChar = 0; /// password character - 0 for normal editor, some character, e.g. '*' to hide text by replacing all characters with this char @@ -1972,17 +1976,27 @@ class EditLine : EditWidgetBase { //Point sz = font.textSize(text); _measuredText = applyPasswordChar(text); _measuredTextWidths.length = _measuredText.length; - int charsMeasured = font.measureText(_measuredText, _measuredTextWidths, int.max, tabSize); + int charsMeasured = font.measureText(_measuredText, _measuredTextWidths, MAX_WIDTH_UNSPECIFIED, tabSize); _measuredTextSize.x = charsMeasured > 0 ? _measuredTextWidths[charsMeasured - 1]: 0; _measuredTextSize.y = font.height; return _measuredTextSize; } + + protected Point measureTextToSetWidgetSize() { + FontRef font = font(); + _measuredTextToSetWidgetSizeWidths.length = _textToSetWidgetSize.length; + int charsMeasured = font.measureText(_textToSetWidgetSize, _measuredTextToSetWidgetSizeWidths, MAX_WIDTH_UNSPECIFIED, tabSize); + _measuredTextToSetWidgetSize.x = charsMeasured > 0 ? _measuredTextToSetWidgetSizeWidths[charsMeasured - 1]: 0; + _measuredTextToSetWidgetSize.y = font.height; + return _measuredTextToSetWidgetSize; + } /// measure override void measure(int parentWidth, int parentHeight) { updateFontProps(); measureVisibleText(); - measuredContent(parentWidth, parentHeight, _measuredTextSize.x + _leftPaneWidth, _measuredTextSize.y); + measureTextToSetWidgetSize(); + measuredContent(parentWidth, parentHeight, _measuredTextToSetWidgetSize.x + _leftPaneWidth, _measuredTextToSetWidgetSize.y); } override bool handleAction(const Action a) { @@ -2128,6 +2142,10 @@ class EditBox : EditWidgetBase { protected int[] _visibleLinesWidths; // width (in pixels) of visible lines protected CustomCharProps[][] _visibleLinesHighlights; protected CustomCharProps[][] _visibleLinesHighlightsBuf; + + protected Point _measuredTextToSetWidgetSize; + protected dstring _textToSetWidgetSize = "aaaaa/naaaaa"d; + protected int[] _measuredTextToSetWidgetSizeWidths; override protected int lineCount() { return _content.length; @@ -2641,6 +2659,16 @@ class EditBox : EditWidgetBase { return textSz; } + // override to set minimum scrollwidget size - default 100x100 + override protected Point minimumVisibleContentSize() { + FontRef font = font(); + _measuredTextToSetWidgetSizeWidths.length = _textToSetWidgetSize.length; + int charsMeasured = font.measureText(_textToSetWidgetSize, _measuredTextToSetWidgetSizeWidths, MAX_WIDTH_UNSPECIFIED, tabSize); + _measuredTextToSetWidgetSize.x = charsMeasured > 0 ? _measuredTextToSetWidgetSizeWidths[charsMeasured - 1]: 0; + _measuredTextToSetWidgetSize.y = font.height; + return _measuredTextToSetWidgetSize; + } + /// measure override void measure(int parentWidth, int parentHeight) { if (visibility == Visibility.Gone) { @@ -2657,8 +2685,6 @@ class EditBox : EditWidgetBase { } super.measure(parentWidth, parentHeight); - // do we need to add vsbwidth, hsbheight ??? - //measuredContent(parentWidth, parentHeight, textSz.x + vsbwidth, textSz.y + hsbheight); } diff --git a/src/dlangui/widgets/grid.d b/src/dlangui/widgets/grid.d index 1d4f9177..27844b0b 100644 --- a/src/dlangui/widgets/grid.d +++ b/src/dlangui/widgets/grid.d @@ -1533,6 +1533,29 @@ class GridWidgetBase : ScrollWidgetBase, GridModelAdapter, MenuItemActionHandler sz.y += _rowHeights[i]; return sz; } + + /// calculate minimum size of widget + override Point minimumVisibleContentSize() { + Point sz; + if (_cols == 0 || _rows == 0) { + sz.x = 100; + sz.y = 100; + return sz; + } + // width: + if (_cols < 2) + sz.x = _colWidths[0]; + else + sz.x = _colWidths[0] + _colWidths[1]; + + // height + if (_rows < 2) + sz.y = _rowHeights[0]; + else + sz.y = _rowHeights[0] + _rowHeights[1]; + + return sz; + } override protected void drawClient(DrawBuf buf) { if (!_cols || !_rows) diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index 29e33249..5d12d08e 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -942,6 +942,11 @@ class ListWidget : WidgetGroup, OnScrollHandler, OnAdapterChangeHandler { if (_adapter) _adapter.onThemeChanged(); } + + /// sets minimum size for the list, override to change + Point minimumVisibleContentSize() { + return Point(100, 100); + } /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). override void measure(int parentWidth, int parentHeight) { @@ -953,6 +958,15 @@ class ListWidget : WidgetGroup, OnScrollHandler, OnAdapterChangeHandler { _itemSizes.length = itemCount; Rect m = margins; Rect p = padding; + + // set widget area to small when first measure + if (parentWidth == SIZE_UNSPECIFIED && parentHeight == SIZE_UNSPECIFIED) + { + Point sz = minimumVisibleContentSize; + measuredContent(parentWidth, parentHeight, sz.x, sz.y); + return; + } + // calc size constraints for children int pwidth = parentWidth; int pheight = parentHeight; @@ -1041,6 +1055,7 @@ class ListWidget : WidgetGroup, OnScrollHandler, OnAdapterChangeHandler { } } measuredContent(parentWidth, parentHeight, sz.x + _sbsz.x, sz.y + _sbsz.y); + if (_scrollbar.visibility == oldScrollbarVisibility) { _needLayout = oldNeedLayout; _scrollbar.cancelLayout(); diff --git a/src/dlangui/widgets/scroll.d b/src/dlangui/widgets/scroll.d index 966b23d5..63d4bc6c 100644 --- a/src/dlangui/widgets/scroll.d +++ b/src/dlangui/widgets/scroll.d @@ -273,6 +273,11 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { return sz; } + // override to set minimum scrollwidget size - default 100x100 + Point minimumVisibleContentSize() { + return Point(100,100); + } + /// 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) { @@ -280,6 +285,7 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { } Rect m = margins; Rect p = padding; + // calc size constraints for children int pwidth = parentWidth; int pheight = parentHeight; @@ -293,13 +299,14 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { if (_vscrollbar && _vscrollbarMode == ScrollBarMode.Visible) { _vscrollbar.measure(pwidth, pheight); } - Point sz = fullContentSize(); + Point sz = minimumVisibleContentSize(); if (_hscrollbar && _hscrollbarMode == ScrollBarMode.Visible) { sz.y += _hscrollbar.measuredHeight; } if (_vscrollbar && _vscrollbarMode == ScrollBarMode.Visible) { sz.x += _vscrollbar.measuredWidth; } + measuredContent(parentWidth, parentHeight, sz.x, sz.y); } diff --git a/src/dlangui/widgets/tree.d b/src/dlangui/widgets/tree.d index b5425ca6..f0453a19 100644 --- a/src/dlangui/widgets/tree.d +++ b/src/dlangui/widgets/tree.d @@ -813,6 +813,10 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener, OnTreeStateCh super.layout(rc); } + override Point minimumVisibleContentSize() { + return Point(200,200); + } + /// 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) {