diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index cd57c575..152d449c 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -2335,9 +2335,73 @@ class EditBox : EditWidgetBase { return sz; } + protected bool _extendRightScrollBound = true; + /// override to determine if scrollbars are needed or not + override protected void checkIfScrollbarsNeeded(ref bool needHScroll, ref bool needVScroll) { + needHScroll = _hscrollbar && (_hscrollbarMode == ScrollBarMode.Visible || _hscrollbarMode == ScrollBarMode.Auto); + needVScroll = _vscrollbar && (_vscrollbarMode == ScrollBarMode.Visible || _vscrollbarMode == ScrollBarMode.Auto); + if (!needHScroll && !needVScroll) + return; // not needed + if (_hscrollbarMode != ScrollBarMode.Auto && _vscrollbarMode != ScrollBarMode.Auto) + return; // no auto scrollbars + // either h or v scrollbar is in auto mode + + int hsbHeight = _hscrollbar.measuredHeight; + int vsbWidth = _hscrollbar.measuredWidth; + + int visibleLines = _lineHeight > 0 ? (_clientRect.height / _lineHeight) : 1; // fully visible lines + if (visibleLines < 1) + visibleLines = 1; + int visibleLinesWithScrollbar = _lineHeight > 0 ? ((_clientRect.height - hsbHeight) / _lineHeight) : 1; // fully visible lines + if (visibleLinesWithScrollbar < 1) + visibleLinesWithScrollbar = 1; + + // either h or v scrollbar is in auto mode + //Point contentSize = fullContentSize(); + int contentWidth = _maxLineWidth + (_extendRightScrollBound ? _clientRect.width / 16 : 0); + int contentHeight = _content.length; + + int clientWidth = _clientRect.width; + int clientHeight = visibleLines; + + int clientWidthWithScrollbar = clientWidth - vsbWidth; + int clientHeightWithScrollbar = visibleLinesWithScrollbar; + + if (_hscrollbarMode == ScrollBarMode.Auto && _vscrollbarMode == ScrollBarMode.Auto) { + // both scrollbars in auto mode + bool xFits = contentWidth <= clientWidth; + bool yFits = contentHeight <= clientHeight; + if (!xFits && !yFits) { + // none fits, need both scrollbars + } else if (xFits && yFits) { + // everything fits! + needHScroll = false; + needVScroll = false; + } else if (xFits) { + // only X fits + if (contentWidth <= clientWidthWithScrollbar) + needHScroll = false; // disable hscroll + } else { // yFits + // only Y fits + if (contentHeight <= clientHeightWithScrollbar) + needVScroll = false; // disable vscroll + } + } else if (_hscrollbarMode == ScrollBarMode.Auto) { + // only hscroll is in auto mode + if (needVScroll) + clientWidth = clientWidthWithScrollbar; + needHScroll = contentWidth > clientWidth; + } else { + // only vscroll is in auto mode + if (needHScroll) + clientHeight = clientHeightWithScrollbar; + needVScroll = contentHeight > clientHeight; + } + } + /// update horizontal scrollbar widget position override protected void updateHScrollBar() { - _hscrollbar.setRange(0, _maxLineWidth + _clientRect.width / 4); + _hscrollbar.setRange(0, _maxLineWidth + (_extendRightScrollBound ? _clientRect.width / 16 : 0)); _hscrollbar.pageSize = _clientRect.width; _hscrollbar.position = _scrollPos.x; } @@ -2347,7 +2411,7 @@ class EditBox : EditWidgetBase { int visibleLines = _lineHeight ? _clientRect.height / _lineHeight : 1; // fully visible lines if (visibleLines < 1) visibleLines = 1; - _vscrollbar.setRange(0, _content.length - 1); + _vscrollbar.setRange(0, _content.length); _vscrollbar.pageSize = visibleLines; _vscrollbar.position = _firstVisibleLine; } @@ -2743,9 +2807,9 @@ class EditBox : EditWidgetBase { /// calculate full content size in pixels override Point fullContentSize() { Point textSz = measureVisibleText(); - int maxy = _lineHeight * 5; // limit measured height - if (textSz.y > maxy) - textSz.y = maxy; + //int maxy = _lineHeight * 5; // limit measured height + //if (textSz.y > maxy) + // textSz.y = maxy; return textSz; } diff --git a/src/dlangui/widgets/scroll.d b/src/dlangui/widgets/scroll.d index 82ec632a..13018ed4 100644 --- a/src/dlangui/widgets/scroll.d +++ b/src/dlangui/widgets/scroll.d @@ -169,6 +169,27 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { } } + protected bool _insideChangeScrollbarVisibility; + protected void checkIfNeededToChangeScrollbarVisibility() { + if (_insideChangeScrollbarVisibility) + return; + bool needHScroll = false; + bool needVScroll = false; + checkIfScrollbarsNeeded(needHScroll, needVScroll); + bool hscrollVisible = _hscrollbar && _hscrollbar.visibility == Visibility.Visible; + bool vscrollVisible = _vscrollbar && _vscrollbar.visibility == Visibility.Visible; + bool needChange = false; + if (_hscrollbar && hscrollVisible != needHScroll) + needChange = true; + if (_vscrollbar && vscrollVisible != needVScroll) + needChange = true; + if (needChange) { + _insideChangeScrollbarVisibility = true; + layout(_pos); + _insideChangeScrollbarVisibility = false; + } + } + /// update scrollbar positions protected void updateScrollBars() { if (_hscrollbar) { @@ -177,6 +198,7 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { if (_vscrollbar) { updateVScrollBar(); } + checkIfNeededToChangeScrollbarVisibility(); } public @property ScrollBar hscrollbar() { return _hscrollbar; } @@ -306,12 +328,18 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { int vsbw = 0; int hsbh = 0; if (_hscrollbar && (_hscrollbarMode == ScrollBarMode.Visible || _hscrollbarMode == ScrollBarMode.Auto)) { + Visibility oldVisibility = _hscrollbar.visibility; + _hscrollbar.visibility = Visibility.Visible; _hscrollbar.measure(pwidth, pheight); hsbh = _hscrollbar.measuredHeight; + _hscrollbar.visibility = oldVisibility; } if (_vscrollbar && (_vscrollbarMode == ScrollBarMode.Visible || _vscrollbarMode == ScrollBarMode.Auto)) { + Visibility oldVisibility = _vscrollbar.visibility; + _vscrollbar.visibility = Visibility.Visible; _vscrollbar.measure(pwidth, pheight); vsbw = _vscrollbar.measuredWidth; + _vscrollbar.visibility = oldVisibility; } Point sz = minimumVisibleContentSize(); @@ -329,6 +357,59 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { protected void handleClientRectLayout(ref Rect rc) { } + /// override to determine if scrollbars are needed or not + protected void checkIfScrollbarsNeeded(ref bool needHScroll, ref bool needVScroll) { + needHScroll = _hscrollbar && (_hscrollbarMode == ScrollBarMode.Visible || _hscrollbarMode == ScrollBarMode.Auto); + needVScroll = _vscrollbar && (_vscrollbarMode == ScrollBarMode.Visible || _vscrollbarMode == ScrollBarMode.Auto); + if (!needHScroll && !needVScroll) + return; // not needed + if (_hscrollbarMode != ScrollBarMode.Auto && _vscrollbarMode != ScrollBarMode.Auto) + return; // no auto scrollbars + // either h or v scrollbar is in auto mode + Point contentSize = fullContentSize(); + int contentWidth = contentSize.x; + int contentHeight = contentSize.y; + int clientWidth = _clientRect.width; + int clientHeight = _clientRect.height; + + int hsbHeight = _hscrollbar.measuredHeight; + int vsbWidth = _hscrollbar.measuredWidth; + + int clientWidthWithScrollbar = clientWidth - vsbWidth; + int clientHeightWithScrollbar = clientHeight - hsbHeight; + + if (_hscrollbarMode == ScrollBarMode.Auto && _vscrollbarMode == ScrollBarMode.Auto) { + // both scrollbars in auto mode + bool xFits = contentWidth <= clientWidth; + bool yFits = contentHeight <= clientHeight; + if (!xFits && !yFits) { + // none fits, need both scrollbars + } else if (xFits && yFits) { + // everything fits! + needHScroll = false; + needVScroll = false; + } else if (xFits) { + // only X fits + if (contentWidth <= clientWidthWithScrollbar) + needHScroll = false; // disable hscroll + } else { // yFits + // only Y fits + if (contentHeight <= clientHeightWithScrollbar) + needVScroll = false; // disable vscroll + } + } else if (_hscrollbarMode == ScrollBarMode.Auto) { + // only hscroll is in auto mode + if (needVScroll) + clientWidth = clientWidthWithScrollbar; + needHScroll = contentWidth > clientWidth; + } else { + // only vscroll is in auto mode + if (needHScroll) + clientHeight = clientHeightWithScrollbar; + needVScroll = contentHeight > clientHeight; + } + } + /// 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) { @@ -338,17 +419,16 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { _needLayout = false; applyMargins(rc); applyPadding(rc); - Point sz = fullContentSize(); - bool needHscroll = _hscrollbarMode != ScrollBarMode.External && _hscrollbarMode != ScrollBarMode.Invisible && sz.x > rc.width; - bool needVscroll = _vscrollbarMode != ScrollBarMode.External && _vscrollbarMode != ScrollBarMode.Invisible && sz.y > rc.height; - if (needVscroll && _vscrollbarMode != ScrollBarMode.Invisible) - needHscroll = sz.x > rc.width - _vscrollbar.measuredWidth; - if (needHscroll && _hscrollbarMode != ScrollBarMode.Invisible) - needVscroll = sz.y > rc.height - _hscrollbar.measuredHeight; - if (needVscroll && _vscrollbarMode != ScrollBarMode.Invisible) - needHscroll = sz.x > rc.width - _vscrollbar.measuredWidth; - needVscroll = needVscroll || (_vscrollbarMode == ScrollBarMode.Visible); - needHscroll = needHscroll || (_hscrollbarMode == ScrollBarMode.Visible); + + // client area - initial setup w/o scrollbars + _clientRect = rc; + handleClientRectLayout(_clientRect); + + bool needHscroll; + bool needVscroll; + + checkIfScrollbarsNeeded(needHscroll, needVscroll); + // scrollbars Rect vsbrc = rc; vsbrc.left = vsbrc.right - (needVscroll ? _vscrollbar.measuredWidth : 0); @@ -364,13 +444,13 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler { _hscrollbar.visibility = needHscroll ? Visibility.Visible : Visibility.Gone; _hscrollbar.layout(hsbrc); } - // client area + _clientRect = rc; - handleClientRectLayout(_clientRect); if (needVscroll) _clientRect.right = vsbrc.left; if (needHscroll) _clientRect.bottom = hsbrc.top; + handleClientRectLayout(_clientRect); updateScrollBars(); } diff --git a/src/dlangui/widgets/scrollbar.d b/src/dlangui/widgets/scrollbar.d index 9d75bc2d..74603036 100644 --- a/src/dlangui/widgets/scrollbar.d +++ b/src/dlangui/widgets/scrollbar.d @@ -282,6 +282,11 @@ class ScrollBar : AbstractSlider, OnClickHandler { return sendScrollEvent(ScrollAction.SliderMoved, currentPosition); } + /// true if full scroll range is visible, and no need of scrolling at all + @property bool fullRangeVisible() { + return _pageSize >= _maxValue - _minValue; + } + private bool calcButtonSizes(int availableSize, ref int spaceBackSize, ref int spaceForwardSize, ref int indicatorSize) { int dv = _maxValue - _minValue; if (_pageSize >= dv) { diff --git a/src/dlangui/widgets/srcedit.d b/src/dlangui/widgets/srcedit.d index 0e5f36a8..90eb61a5 100644 --- a/src/dlangui/widgets/srcedit.d +++ b/src/dlangui/widgets/srcedit.d @@ -27,6 +27,7 @@ enum DEFAULT_SOURCE_EDIT_FONT_FACES = "Menlo,Consolas,DejaVuSansMono,DejaVu Sans class SourceEdit : EditBox { this(string ID) { super(ID); + _extendRightScrollBound = true; fontFace = DEFAULT_SOURCE_EDIT_FONT_FACES; //fontFace = "Consolas,Lucida Console,Courier New"; fontFamily = FontFamily.MonoSpace; diff --git a/views/DLANGUI_VERSION b/views/DLANGUI_VERSION index 3bc8a512..24fcc237 100644 --- a/views/DLANGUI_VERSION +++ b/views/DLANGUI_VERSION @@ -1 +1 @@ -v0.9.134 \ No newline at end of file +v0.9.136 \ No newline at end of file