From 82d9f508418d3d622861afc797967daf4f986267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Wed, 8 Apr 2009 22:10:27 +0000 Subject: [PATCH] Some BTextView fixes, most importantly the annoying up/down ping pong when triggering auto-scrolling in BTextControls... * _BTextInput_::MinSize() added 1 to the line height, but when aligning the text rect, at least one pixel is added at the top and bottom, which makes for at least two extra pixels. * BTextView::_PerformAutoScrolling() had some code which was supposed to prevent from out-of-bounds scrolling, but the bottom maximum coordinate was not correctly calculated. This and the above item led to the ping-pong effect. * Additionally, I prevented scrolling vertically for one-line text views completely. * On mouse-up, reset the cursor. It may have to be the I-Beam cursor again, for example after de-selecting. * While mouse tracking the selection, always use the I-Beam cursor. * Also when mouse tracking, do not use the minimum/maximum text offset when the mouse is above/below the text rect. Do this only when it's also outside on left/right sides. This is less irritating and works like on other platforms. It means the first/last line can still be selected, without having to constrain the mouse to the inside of the text view. * When calculating the selection region, don't make the bottom one pixel too far up. Lines which contain glyphs that extend below the base-line have one more pixel below the glyphs that is inverted now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30046 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kits/interface/TextInput.cpp | 4 +- src/kits/interface/TextView.cpp | 88 +++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/kits/interface/TextInput.cpp b/src/kits/interface/TextInput.cpp index 5e555776ca..30c5e6d510 100644 --- a/src/kits/interface/TextInput.cpp +++ b/src/kits/interface/TextInput.cpp @@ -153,7 +153,9 @@ BSize _BTextInput_::MinSize() { BSize min; - min.height = ceilf(LineHeight(0) + 1.0); + min.height = ceilf(LineHeight(0) + 2.0); + // we always add at least one pixel vertical inset top/bottom for + // the text rect. min.width = min.height * 3; return BLayoutUtils::ComposeSize(ExplicitMinSize(), min); } diff --git a/src/kits/interface/TextView.cpp b/src/kits/interface/TextView.cpp index 1f932e373d..b3e851513a 100644 --- a/src/kits/interface/TextView.cpp +++ b/src/kits/interface/TextView.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008, Haiku Inc. All rights reserved. + * Copyright 2001-2009, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -1831,14 +1831,27 @@ BTextView::OffsetAt(BPoint point) const const int32 textLength = fText->Length(); // should we even bother? - if (point.y >= fTextRect.bottom) + if (point.y >= fTextRect.bottom && point.x >= fTextRect.right) return textLength; - else if (point.y < fTextRect.top) + else if (point.y < fTextRect.top && point.x <= fTextRect.left) return 0; +#define COMPILE_PROBABLY_BAD_CODE 1 + +#if COMPILE_PROBABLY_BAD_CODE +// NOTE: I have not been able to test what happens if all this is removed. +// For one-line text views (BTextControl), it works just fine. But I would +// need to check StyledEdit or something in various situations (new lines +// at end and other stuff)... + // special case one line text views + if (CountLines() <= 1) + point.y = fTextRect.top; +#endif + int32 lineNum = LineAt(point); STELine* line = (*fLines)[lineNum]; +#if COMPILE_PROBABLY_BAD_CODE // special case: if point is within the text rect and PixelToLine() // tells us that it's on the last line, but if point is actually // lower than the bottom of the last line, return the last offset @@ -1847,6 +1860,7 @@ BTextView::OffsetAt(BPoint point) const if (point.y >= ((line + 1)->origin + fTextRect.top)) return textLength; } +#endif // convert to text rect coordinates if (fAlignment != B_ALIGN_LEFT) { @@ -2033,14 +2047,14 @@ BTextView::GetTextRegion(int32 startOffset, int32 endOffset, BRegion *outRegion) selRect.left = max_c(startPt.x, fTextRect.left); selRect.top = startPt.y; selRect.right = endPt.x - 1.0; - selRect.bottom = endPt.y + endLineHeight - 1.0; + selRect.bottom = endPt.y + endLineHeight; outRegion->Include(selRect); } else { // more than one line in the specified offset range selRect.left = max_c(startPt.x, fTextRect.left); selRect.top = startPt.y; selRect.right = fTextRect.right; - selRect.bottom = startPt.y + startLineHeight - 1.0; + selRect.bottom = startPt.y + startLineHeight; outRegion->Include(selRect); if (startPt.y + startLineHeight < endPt.y) { @@ -2048,14 +2062,14 @@ BTextView::GetTextRegion(int32 startOffset, int32 endOffset, BRegion *outRegion) selRect.left = fTextRect.left; selRect.top = startPt.y + startLineHeight; selRect.right = fTextRect.right; - selRect.bottom = endPt.y - 1.0; + selRect.bottom = endPt.y; outRegion->Include(selRect); } selRect.left = fTextRect.left; selRect.top = endPt.y; selRect.right = endPt.x - 1.0; - selRect.bottom = endPt.y + endLineHeight - 1.0; + selRect.bottom = endPt.y + endLineHeight; outRegion->Include(selRect); } } @@ -2145,11 +2159,16 @@ BTextView::SetTextRect(BRect rect) if (rect == fTextRect) return; + // The text rect height is always calculated anyways to contain the + // whole text. But it is used here to remember the inset the user + // wanted. bool needsRefresh = fTextRect.left != rect.left || fTextRect.right != rect.right || fTextRect.top != rect.top; fLayoutData->UpdateInsets(Bounds().OffsetToCopy(B_ORIGIN), rect); + // When we already know we don't want to recalculate anything, we + // can just ignore the bottom coordinate the user provided. if (!needsRefresh) rect.bottom = fTextRect.bottom; @@ -4298,6 +4317,8 @@ BTextView::_PerformMouseUp(BPoint where) Select(fTrackingMouse->clickOffset, fTrackingMouse->clickOffset); _StopMouseTracking(); + // adjust cursor if necessary + _TrackMouse(where, NULL, true); return true; } @@ -4394,9 +4415,10 @@ BTextView::_TrackMouse(BPoint where, const BMessage *message, bool force) if (message && AcceptsDrop(message)) _TrackDrag(where); - else if ((fSelectable || fEditable) && !textRegion.Contains(where)) + else if ((fSelectable || fEditable) + && (fTrackingMouse != NULL || !textRegion.Contains(where))) { SetViewCursor(B_CURSOR_I_BEAM, force); - else + } else SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, force); } @@ -4518,7 +4540,7 @@ BTextView::_PerformAutoScrolling() { // Scroll the view a bit if mouse is outside the view bounds BRect bounds = Bounds(); - BPoint scrollBy; + BPoint scrollBy(B_ORIGIN); BPoint constraint = fWhere; constraint.ConstrainTo(bounds); @@ -4533,27 +4555,35 @@ BTextView::_PerformAutoScrolling() scrollBy.x = -value; } - float lineHeight = 0; - float vertDiff = 0; - if (fWhere.y > bounds.bottom) { - lineHeight = LineHeight(LineAt(bounds.LeftBottom())); - vertDiff = fWhere.y - bounds.bottom; - } else if (fWhere.y < bounds.top) { - lineHeight = LineHeight(LineAt(bounds.LeftTop())); - vertDiff = fWhere.y - bounds.top; // negative value + if (CountLines() > 1) { + // scroll in Y only if multiple lines! + + float lineHeight = 0; + float vertDiff = 0; + if (fWhere.y > bounds.bottom) { + lineHeight = LineHeight(LineAt(bounds.LeftBottom())); + vertDiff = fWhere.y - bounds.bottom; + } else if (fWhere.y < bounds.top) { + lineHeight = LineHeight(LineAt(bounds.LeftTop())); + vertDiff = fWhere.y - bounds.top; // negative value + } + // Always scroll vertically line by line or by multiples of that + // based on the distance of the cursor from the border of the view + // TODO: Refine this, I can't even remember how beos works here + scrollBy.y = lineHeight > 0 ? lineHeight * (int32)(floorf(vertDiff) + / lineHeight) : 0; + + // prevent from scrolling out of view + if (scrollBy.y != 0.0) { + float bottomMax = floorf(fTextRect.bottom + + fLayoutData->bottomInset); + if (bounds.bottom + scrollBy.y > bottomMax) + scrollBy.y = bottomMax - bounds.bottom; + else if (bounds.top + scrollBy.y < 0) + scrollBy.y = -bounds.top; + } } - // Always scroll vertically line by line or by multiples of that - // based on the distance of the cursor from the border of the view - // TODO: Refine this, I can't even remember how beos works here - scrollBy.y = lineHeight > 0 ? lineHeight * (int32)(floorf(vertDiff) - / lineHeight) : 0; - - if (bounds.bottom + scrollBy.y > fTextRect.Height()) - scrollBy.y = fTextRect.Height() - bounds.bottom; - else if (bounds.top + scrollBy.y < 0) - scrollBy.y = -bounds.top; - if (scrollBy != B_ORIGIN) ScrollBy(scrollBy.x, scrollBy.y); }