BTextView: Fix Tracker edit name
Don't resize text view in FrameResized() if resizable, this is done in _AutoResize() instead. Set text rect width to width of max line when word-wrap is off. Text rect width shrinks to the width of the text matching behavior of BeOS R5 and previous Haiku. This fixes Tracker Edit name. Limit max width to column width in list mode or 30em in icon mode. Filter paste messages limiting to max width in Tracker Edit name. General BTextView fixes: As a consequence of the text rect shrinking to fit the text, adjust highlighting to go at least to edge of the view even if text rect width is narrower. Extend the invalidation area beyond text rect when redrawing to include highlighted areas. Text views behave properly when overflow occurs i.e. when you type text off the end of the text view. The text is nudged over as you type/scroll so that the previous text is visible. This sorta worked before but now works better. Fix text rect centering by replacing switch with BLayoutUtils::AlignOnRect(). Coalesce consecutive draw calls when inserting and deleting text to prevent flashing for example when resizing the window. Redraw text when the text view scrolls fixing a bug I noticed in StyledEdit. Workaround negative height Beezer bug. Fixes #16642, #16476 Change-Id: I2d32d6039944d2dc3218ce4de71f2966cc98c866 Reviewed-on: https://review.haiku-os.org/c/haiku/+/3642 Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org> Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
parent
7d961f9746
commit
7912dad947
@ -302,6 +302,7 @@ private:
|
||||
int32 scrollTo = INT32_MIN);
|
||||
void _RecalculateLineBreaks(int32* startLine,
|
||||
int32* endLine);
|
||||
void _ValidateTextRect();
|
||||
int32 _FindLineBreak(int32 fromOffset,
|
||||
float* _ascent, float* _descent,
|
||||
float* inOutWidth);
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Alignment.h>
|
||||
#include <Application.h>
|
||||
#include <Beep.h>
|
||||
#include <Bitmap.h>
|
||||
@ -509,12 +510,7 @@ BTextView::AttachedToWindow()
|
||||
fDragOffset = -1;
|
||||
fActive = false;
|
||||
|
||||
// text rect right must be greater than left
|
||||
if (fTextRect.right <= fTextRect.left)
|
||||
fTextRect.right = fTextRect.left + 1;
|
||||
// text rect bottom must be greater than top
|
||||
if (fTextRect.bottom <= fTextRect.top)
|
||||
fTextRect.bottom = fTextRect.top + 1;
|
||||
_ValidateTextRect();
|
||||
|
||||
_AutoResize(true);
|
||||
|
||||
@ -808,53 +804,36 @@ BTextView::FrameResized(float newWidth, float newHeight)
|
||||
{
|
||||
BView::FrameResized(newWidth, newHeight);
|
||||
|
||||
// frame resized in _AutoResize() instead
|
||||
if (fResizable)
|
||||
return;
|
||||
|
||||
if (fWrap) {
|
||||
// recalculate line breaks
|
||||
// will update scroll bars if text rect changes
|
||||
_ResetTextRect();
|
||||
} else {
|
||||
// don't recalculate line breaks,
|
||||
// move text rect into position and redraw.
|
||||
|
||||
BRect oldTextRect(fTextRect);
|
||||
BRect bounds(0, 0, newWidth, newHeight);
|
||||
float dataWidth = fLayoutData->leftInset
|
||||
+ fTextRect.Width() + fLayoutData->rightInset;
|
||||
newWidth = std::max(dataWidth, newWidth);
|
||||
|
||||
switch (fAlignment) {
|
||||
default:
|
||||
case B_ALIGN_LEFT:
|
||||
// move text rect to left top corner
|
||||
fTextRect.OffsetTo(bounds.left, fTextRect.top);
|
||||
fTextRect.OffsetBy(fLayoutData->leftInset, 0);
|
||||
break;
|
||||
// align rect
|
||||
BRect rect(fLayoutData->leftInset, fLayoutData->topInset,
|
||||
newWidth - fLayoutData->rightInset,
|
||||
newHeight - fLayoutData->bottomInset);
|
||||
|
||||
case B_ALIGN_RIGHT:
|
||||
{
|
||||
// move text rect to right top corner
|
||||
float textRectWidthPlusInsets = fTextRect.Width()
|
||||
+ fLayoutData->leftInset + fLayoutData->rightInset;
|
||||
float maxWidth = std::max(bounds.Width(),
|
||||
textRectWidthPlusInsets);
|
||||
fTextRect.OffsetTo(maxWidth - fTextRect.Width(),
|
||||
fTextRect.top);
|
||||
fTextRect.OffsetBy(-fLayoutData->rightInset, 0);
|
||||
break;
|
||||
}
|
||||
rect = BLayoutUtils::AlignOnRect(rect,
|
||||
BSize(fTextRect.Width(), fTextRect.Height()),
|
||||
BAlignment(fAlignment, B_ALIGN_TOP));
|
||||
fTextRect.OffsetTo(rect.left, rect.top);
|
||||
|
||||
case B_ALIGN_CENTER:
|
||||
// move text rect to center
|
||||
fTextRect.OffsetTo(ceilf((fTextRect.Width() - bounds.Width())
|
||||
/ 2), fTextRect.top);
|
||||
break;
|
||||
}
|
||||
|
||||
// only redraw if text rect changes
|
||||
if (fTextRect != oldTextRect) {
|
||||
// Make sure that the dirty area outside the text is redrawn too.
|
||||
BRegion dirty(oldTextRect | fTextRect);
|
||||
Invalidate(&dirty);
|
||||
}
|
||||
// must invalidate whole thing because of highlighting
|
||||
Invalidate();
|
||||
_UpdateScrollbars();
|
||||
}
|
||||
|
||||
_UpdateScrollbars();
|
||||
}
|
||||
|
||||
|
||||
@ -1187,6 +1166,7 @@ BTextView::SetText(const char* text, int32 length, const text_run_array* runs)
|
||||
if (!Bounds().IsValid()) {
|
||||
ResizeTo(LineWidth(0), LineHeight(0));
|
||||
fTextRect = Bounds();
|
||||
_ValidateTextRect();
|
||||
_UpdateInsets(fTextRect);
|
||||
}
|
||||
|
||||
@ -2125,35 +2105,44 @@ BTextView::GetTextRegion(int32 startOffset, int32 endOffset,
|
||||
endLineHeight = ceilf(endLineHeight);
|
||||
|
||||
BRect selRect;
|
||||
const BRect bounds(Bounds());
|
||||
|
||||
if (startPt.y == endPt.y) {
|
||||
// this is a one-line region
|
||||
selRect.left = std::max(startPt.x, fTextRect.left);
|
||||
selRect.left = startPt.x;
|
||||
selRect.top = startPt.y;
|
||||
selRect.right = endPt.x - 1.0;
|
||||
selRect.bottom = endPt.y + endLineHeight - 1.0;
|
||||
selRect.right = endPt.x - 1;
|
||||
selRect.bottom = endPt.y + endLineHeight - 1;
|
||||
outRegion->Include(selRect);
|
||||
} else {
|
||||
// more than one line in the specified offset range
|
||||
selRect.left = std::max(startPt.x, fTextRect.left);
|
||||
|
||||
// include first line from start of selection to end of window
|
||||
selRect.left = startPt.x;
|
||||
selRect.top = startPt.y;
|
||||
selRect.right = fTextRect.right;
|
||||
selRect.bottom = startPt.y + startLineHeight - 1.0;
|
||||
selRect.right = std::max(fTextRect.right,
|
||||
bounds.right - fLayoutData->rightInset);
|
||||
selRect.bottom = startPt.y + startLineHeight - 1;
|
||||
outRegion->Include(selRect);
|
||||
|
||||
if (startPt.y + startLineHeight < endPt.y) {
|
||||
// more than two lines in the range
|
||||
selRect.left = fTextRect.left;
|
||||
// include middle lines from start to end of window
|
||||
selRect.left = std::min(fTextRect.left,
|
||||
bounds.left + fLayoutData->leftInset);
|
||||
selRect.top = startPt.y + startLineHeight;
|
||||
selRect.right = fTextRect.right;
|
||||
selRect.bottom = endPt.y - 1.0;
|
||||
selRect.right = std::max(fTextRect.right,
|
||||
bounds.right - fLayoutData->rightInset);
|
||||
selRect.bottom = endPt.y - 1;
|
||||
outRegion->Include(selRect);
|
||||
}
|
||||
|
||||
selRect.left = fTextRect.left;
|
||||
// include last line start of window to end of selection
|
||||
selRect.left = std::min(fTextRect.left,
|
||||
bounds.left + fLayoutData->leftInset);
|
||||
selRect.top = endPt.y;
|
||||
selRect.right = endPt.x - 1.0;
|
||||
selRect.bottom = endPt.y + endLineHeight - 1.0;
|
||||
selRect.right = endPt.x - 1;
|
||||
selRect.bottom = endPt.y + endLineHeight - 1;
|
||||
outRegion->Include(selRect);
|
||||
}
|
||||
}
|
||||
@ -2164,30 +2153,42 @@ BTextView::ScrollToOffset(int32 offset)
|
||||
{
|
||||
BRect bounds = Bounds();
|
||||
float lineHeight = 0.0;
|
||||
float xDiff = 0.0;
|
||||
float yDiff = 0.0;
|
||||
BPoint point = PointAt(offset, &lineHeight);
|
||||
BPoint scrollBy(B_ORIGIN);
|
||||
|
||||
// horizontal
|
||||
float extraSpace = ceilf(bounds.IntegerWidth() / 2);
|
||||
if (point.x < bounds.left)
|
||||
xDiff = point.x - bounds.right + extraSpace;
|
||||
scrollBy.x = point.x - bounds.right;
|
||||
else if (point.x > bounds.right)
|
||||
xDiff = point.x - bounds.left - extraSpace;
|
||||
scrollBy.x = point.x - bounds.left;
|
||||
|
||||
// vertical
|
||||
if (point.y < bounds.top)
|
||||
yDiff = point.y - bounds.top;
|
||||
else if (point.y + lineHeight > bounds.bottom
|
||||
&& point.y - lineHeight > bounds.top) {
|
||||
yDiff = point.y + lineHeight - bounds.bottom;
|
||||
// prevent from scrolling out of view
|
||||
if (scrollBy.x != 0.0) {
|
||||
float rightMax = fTextRect.right + fLayoutData->rightInset;
|
||||
if (bounds.right + scrollBy.x > rightMax)
|
||||
scrollBy.x = rightMax - bounds.right;
|
||||
float leftMin = fTextRect.left - fLayoutData->leftInset;
|
||||
if (bounds.left + scrollBy.x < leftMin)
|
||||
scrollBy.x = leftMin - bounds.left;
|
||||
}
|
||||
|
||||
// prevent negative scroll offset in y
|
||||
if (bounds.top + yDiff < 0.0)
|
||||
yDiff = -bounds.top;
|
||||
// vertical
|
||||
if (CountLines() > 1) {
|
||||
// scroll in Y only if multiple lines!
|
||||
if (point.y < bounds.top - fLayoutData->topInset)
|
||||
scrollBy.y = point.y - bounds.top - fLayoutData->topInset;
|
||||
else if (point.y + lineHeight > bounds.bottom
|
||||
+ fLayoutData->bottomInset) {
|
||||
scrollBy.y = point.y + lineHeight - bounds.bottom
|
||||
+ fLayoutData->bottomInset;
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBy(xDiff, yDiff);
|
||||
ScrollBy(scrollBy.x, scrollBy.y);
|
||||
|
||||
// Update text rect position and scroll bars
|
||||
if (CountLines() > 1 && !fWrap)
|
||||
FrameResized(Bounds().Width(), Bounds().Height());
|
||||
}
|
||||
|
||||
|
||||
@ -2409,6 +2410,8 @@ BTextView::SetWordWrap(bool wrap)
|
||||
_HideCaret();
|
||||
}
|
||||
|
||||
BRect savedBounds = Bounds();
|
||||
|
||||
fWrap = wrap;
|
||||
if (wrap)
|
||||
_ResetTextRect(); // calls _Refresh
|
||||
@ -2418,6 +2421,10 @@ BTextView::SetWordWrap(bool wrap)
|
||||
if (fEditable)
|
||||
ScrollToOffset(fCaretOffset);
|
||||
|
||||
// redraw text rect and update scroll bars if bounds have changed
|
||||
if (Bounds() != savedBounds)
|
||||
FrameResized(Bounds().Width(), Bounds().Height());
|
||||
|
||||
if (updateOnScreen) {
|
||||
// show the caret, hilite the selection
|
||||
if (fSelStart != fSelEnd) {
|
||||
@ -2491,8 +2498,11 @@ BTextView::SetAlignment(alignment align)
|
||||
fAlignment = align;
|
||||
|
||||
// After setting new alignment, update the view/window
|
||||
if (Window() != NULL)
|
||||
if (Window() != NULL) {
|
||||
FrameResized(Bounds().Width(), Bounds().Height());
|
||||
// text rect position and scroll bars may change
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2557,7 +2567,7 @@ BTextView::MakeResizable(bool resize, BView* resizeView)
|
||||
}
|
||||
// We need to reset the right inset, as otherwise the auto-resize would
|
||||
// get confused about just how wide the textview needs to be.
|
||||
// This seems to be an artefact of how Tracker creates the textview
|
||||
// This seems to be an artifact of how Tracker creates the textview
|
||||
// during a rename action.
|
||||
fLayoutData->rightInset = fLayoutData->leftInset;
|
||||
} else {
|
||||
@ -3185,10 +3195,8 @@ BTextView::_InitObject(BRect textRect, const BFont* initialFont,
|
||||
// if needed.
|
||||
fTextRect = textRect;
|
||||
// NOTE: The only places where text rect is changed:
|
||||
// * width is possibly adjusted in _AutoResize(),
|
||||
// * height is adjusted in _RecalculateLineBreaks().
|
||||
// When used within the layout management framework, the
|
||||
// text rect is changed to maintain constant insets.
|
||||
// * width and height are adjusted in _RecalculateLineBreaks(),
|
||||
// text rect maintains constant insets, use SetInsets() to change.
|
||||
fMinTextRectWidth = fTextRect.Width();
|
||||
// see SetTextRect()
|
||||
fSelStart = fSelEnd = 0;
|
||||
@ -3258,10 +3266,12 @@ BTextView::_HandleBackspace(int32 modifiers)
|
||||
undoBuffer->BackwardErase();
|
||||
}
|
||||
|
||||
// we may draw twice, so turn updates off for now
|
||||
if (Window() != NULL)
|
||||
Window()->DisableUpdates();
|
||||
|
||||
if (fSelStart == fSelEnd) {
|
||||
if (fSelStart == 0)
|
||||
return;
|
||||
else
|
||||
if (fSelStart != 0)
|
||||
fSelStart = _PreviousInitialByte(fSelStart);
|
||||
} else
|
||||
Highlight(fSelStart, fSelEnd);
|
||||
@ -3270,6 +3280,10 @@ BTextView::_HandleBackspace(int32 modifiers)
|
||||
fCaretOffset = fSelEnd = fSelStart;
|
||||
|
||||
_Refresh(fSelStart, fSelEnd, fCaretOffset);
|
||||
|
||||
// turn updates back on
|
||||
if (Window() != NULL)
|
||||
Window()->EnableUpdates();
|
||||
}
|
||||
|
||||
|
||||
@ -3479,10 +3493,12 @@ BTextView::_HandleDelete(int32 modifiers)
|
||||
undoBuffer->ForwardErase();
|
||||
}
|
||||
|
||||
// we may draw twice, so turn updates off for now
|
||||
if (Window() != NULL)
|
||||
Window()->DisableUpdates();
|
||||
|
||||
if (fSelStart == fSelEnd) {
|
||||
if (fSelEnd == fText->Length())
|
||||
return;
|
||||
else
|
||||
if (fSelEnd != fText->Length())
|
||||
fSelEnd = _NextInitialByte(fSelEnd);
|
||||
} else
|
||||
Highlight(fSelStart, fSelEnd);
|
||||
@ -3491,6 +3507,10 @@ BTextView::_HandleDelete(int32 modifiers)
|
||||
fCaretOffset = fSelEnd = fSelStart;
|
||||
|
||||
_Refresh(fSelStart, fSelEnd, fCaretOffset);
|
||||
|
||||
// turn updates back on
|
||||
if (Window() != NULL)
|
||||
Window()->EnableUpdates();
|
||||
}
|
||||
|
||||
|
||||
@ -3694,6 +3714,10 @@ BTextView::_HandleAlphaKey(const char* bytes, int32 numBytes)
|
||||
DeleteText(fSelStart, fSelEnd);
|
||||
}
|
||||
|
||||
// we may draw twice, so turn updates off for now
|
||||
if (Window() != NULL)
|
||||
Window()->DisableUpdates();
|
||||
|
||||
if (fAutoindent && numBytes == 1 && *bytes == B_ENTER) {
|
||||
int32 start, offset;
|
||||
start = offset = OffsetAt(_LineAt(fSelStart));
|
||||
@ -3710,8 +3734,11 @@ BTextView::_HandleAlphaKey(const char* bytes, int32 numBytes)
|
||||
_DoInsertText(bytes, numBytes, fSelStart, NULL);
|
||||
|
||||
fCaretOffset = fSelEnd;
|
||||
|
||||
ScrollToOffset(fCaretOffset);
|
||||
|
||||
// turn updates back on
|
||||
if (Window() != NULL)
|
||||
Window()->EnableUpdates();
|
||||
}
|
||||
|
||||
|
||||
@ -3883,25 +3910,23 @@ BTextView::_RecalculateLineBreaks(int32* startLine, int32* endLine)
|
||||
switch (fAlignment) {
|
||||
default:
|
||||
case B_ALIGN_LEFT:
|
||||
// grow right
|
||||
fTextRect.right = std::max(fTextRect.right,
|
||||
fTextRect.left + fMinTextRectWidth);
|
||||
// move right edge
|
||||
fTextRect.right = fTextRect.left + fMinTextRectWidth;
|
||||
break;
|
||||
|
||||
case B_ALIGN_RIGHT:
|
||||
// grow left
|
||||
fTextRect.left = std::min(fTextRect.left,
|
||||
fTextRect.right - fMinTextRectWidth);
|
||||
// move left edge
|
||||
fTextRect.left = fTextRect.right - fMinTextRectWidth;
|
||||
break;
|
||||
|
||||
case B_ALIGN_CENTER:
|
||||
// grow out
|
||||
if (fMinTextRectWidth > fTextRect.Width()) {
|
||||
fTextRect.InsetBy(ceilf((fTextRect.Width()
|
||||
- fMinTextRectWidth) / 2.0f), 0);
|
||||
}
|
||||
// move both edges
|
||||
fTextRect.InsetBy(roundf((fTextRect.Width()
|
||||
- fMinTextRectWidth) / 2), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
_ValidateTextRect();
|
||||
}
|
||||
|
||||
*endLine = lineIndex - 1;
|
||||
@ -3909,6 +3934,18 @@ BTextView::_RecalculateLineBreaks(int32* startLine, int32* endLine)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTextView::_ValidateTextRect()
|
||||
{
|
||||
// text rect right must be greater than left
|
||||
if (fTextRect.right <= fTextRect.left)
|
||||
fTextRect.right = fTextRect.left + 1;
|
||||
// text rect bottom must be greater than top
|
||||
if (fTextRect.bottom <= fTextRect.top)
|
||||
fTextRect.bottom = fTextRect.top + 1;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
BTextView::_FindLineBreak(int32 fromOffset, float* _ascent, float* _descent,
|
||||
float* inOutWidth)
|
||||
@ -4482,14 +4519,15 @@ BTextView::_DrawLines(int32 startLine, int32 endLine, int32 startOffset,
|
||||
if (!Window())
|
||||
return;
|
||||
|
||||
// clip the text
|
||||
BRect textRect(fTextRect);
|
||||
float minWidth = Bounds().Width() - fLayoutData->leftInset
|
||||
- fLayoutData->rightInset;
|
||||
if (textRect.Width() < minWidth)
|
||||
textRect.right = textRect.left + minWidth;
|
||||
BRect clipRect = Bounds() & textRect;
|
||||
clipRect.InsetBy(-1, -1);
|
||||
const BRect bounds(Bounds());
|
||||
|
||||
// clip the text extending to end of selection
|
||||
BRect clipRect(fTextRect);
|
||||
clipRect.left = std::min(fTextRect.left,
|
||||
bounds.left + fLayoutData->leftInset);
|
||||
clipRect.right = std::max(fTextRect.right,
|
||||
bounds.right - fLayoutData->rightInset);
|
||||
clipRect = bounds & clipRect;
|
||||
|
||||
BRegion newClip;
|
||||
newClip.Set(clipRect);
|
||||
@ -4590,7 +4628,6 @@ BTextView::_RequestDrawLines(int32 startLine, int32 endLine)
|
||||
Bounds().right,
|
||||
to != NULL ? to->origin + fTextRect.top : fTextRect.bottom);
|
||||
Invalidate(invalidRect);
|
||||
Window()->UpdateIfNeeded();
|
||||
}
|
||||
|
||||
|
||||
@ -4939,11 +4976,12 @@ BTextView::_PerformAutoScrolling()
|
||||
|
||||
// prevent from scrolling out of view
|
||||
if (scrollBy.x != 0.0) {
|
||||
float rightMax = floorf(fTextRect.right + fLayoutData->rightInset);
|
||||
float rightMax = fTextRect.right + fLayoutData->rightInset;
|
||||
if (bounds.right + scrollBy.x > rightMax)
|
||||
scrollBy.x = rightMax - bounds.right;
|
||||
if (bounds.left + scrollBy.x < 0)
|
||||
scrollBy.x = -bounds.left;
|
||||
float leftMin = fTextRect.left - fLayoutData->leftInset;
|
||||
if (bounds.left + scrollBy.x < leftMin)
|
||||
scrollBy.x = leftMin - bounds.left;
|
||||
}
|
||||
|
||||
if (CountLines() > 1) {
|
||||
@ -4955,12 +4993,12 @@ BTextView::_PerformAutoScrolling()
|
||||
|
||||
// prevent from scrolling out of view
|
||||
if (scrollBy.y != 0.0) {
|
||||
float bottomMax = floorf(fTextRect.bottom
|
||||
+ fLayoutData->bottomInset);
|
||||
float bottomMax = fTextRect.bottom + fLayoutData->bottomInset;
|
||||
if (bounds.bottom + scrollBy.y > bottomMax)
|
||||
scrollBy.y = bottomMax - bounds.bottom;
|
||||
if (bounds.top + scrollBy.y < 0)
|
||||
scrollBy.y = -bounds.top;
|
||||
float topMin = fTextRect.top - fLayoutData->topInset;
|
||||
if (bounds.top + scrollBy.y < topMin)
|
||||
scrollBy.y = topMin - bounds.top;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5046,40 +5084,33 @@ BTextView::_ScrollTo(float x, float y)
|
||||
void
|
||||
BTextView::_AutoResize(bool redraw)
|
||||
{
|
||||
if (!fResizable)
|
||||
if (!fResizable || fContainerView == NULL)
|
||||
return;
|
||||
|
||||
BRect bounds = Bounds();
|
||||
// NOTE: This container view thing is only used by Tracker.
|
||||
// move container view if not left aligned
|
||||
float oldWidth = Bounds().Width();
|
||||
float newWidth = fLayoutData->leftInset + fTextRect.Width()
|
||||
+ fLayoutData->rightInset;
|
||||
float right = oldWidth - newWidth;
|
||||
|
||||
if (fContainerView != NULL) {
|
||||
// NOTE: This container view thing is only used by Tracker.
|
||||
// move container view if not left aligned
|
||||
float oldWidth = bounds.Width();
|
||||
float newWidth = ceilf(fLayoutData->leftInset
|
||||
+ fTextRect.Width() + fLayoutData->rightInset);
|
||||
if (fAlignment == B_ALIGN_CENTER) {
|
||||
if (fmod(ceilf(newWidth - oldWidth), 2.0) != 0.0)
|
||||
newWidth += 1;
|
||||
if (fAlignment == B_ALIGN_CENTER)
|
||||
fContainerView->MoveBy(roundf(right / 2), 0);
|
||||
else if (fAlignment == B_ALIGN_RIGHT)
|
||||
fContainerView->MoveBy(right, 0);
|
||||
|
||||
fContainerView->MoveBy(ceilf(oldWidth - newWidth) / 2, 0);
|
||||
} else if (fAlignment == B_ALIGN_RIGHT)
|
||||
fContainerView->MoveBy(ceilf(oldWidth - newWidth), 0);
|
||||
// resize container view
|
||||
float grow = newWidth - oldWidth;
|
||||
fContainerView->ResizeBy(grow, 0);
|
||||
|
||||
// resize container view
|
||||
fContainerView->ResizeBy(ceilf(newWidth - oldWidth), 0);
|
||||
}
|
||||
// reposition text view
|
||||
fTextRect.OffsetTo(fLayoutData->leftInset, fLayoutData->topInset);
|
||||
|
||||
// scroll rect to start, there is room for full text
|
||||
ScrollToOffset(0);
|
||||
|
||||
if (redraw)
|
||||
_RequestDrawLines(0, 0);
|
||||
|
||||
// erase any potential left over outside the text rect
|
||||
// (can only be on right hand side)
|
||||
BRect dirty(fTextRect.right + 1, fTextRect.top, bounds.right,
|
||||
fTextRect.bottom);
|
||||
if (dirty.IsValid()) {
|
||||
SetLowColor(ViewColor());
|
||||
FillRect(dirty, B_SOLID_LOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1486,7 +1486,7 @@ BContainerWindow::MessageReceived(BMessage* message)
|
||||
// The selected item is not a BTextView, so forward the
|
||||
// message to the PoseView.
|
||||
if (fPoseView != NULL)
|
||||
fPoseView->MessageReceived(message);
|
||||
PostMessage(message, fPoseView);
|
||||
} else {
|
||||
// Since we catch the generic clipboard shortcuts in a way that
|
||||
// means the BTextView will never get them, we must
|
||||
@ -1497,7 +1497,7 @@ BContainerWindow::MessageReceived(BMessage* message)
|
||||
// recursion.
|
||||
if (message->what == B_CUT || message->what == B_COPY
|
||||
|| message->what == B_PASTE) {
|
||||
view->MessageReceived(message);
|
||||
PostMessage(message, view);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -277,7 +277,8 @@ BPoseView::BPoseView(Model* model, uint32 viewMode)
|
||||
fLastKeyTime(0),
|
||||
fLastDeskbarFrameCheckTime(LONGLONG_MIN),
|
||||
fDeskbarFrame(0, 0, -1, -1),
|
||||
fTextWidgetToCheck(NULL)
|
||||
fTextWidgetToCheck(NULL),
|
||||
fActiveTextWidget(NULL)
|
||||
{
|
||||
fListElemHeight = ceilf(be_plain_font->Size() * 1.65f);
|
||||
|
||||
|
@ -414,6 +414,9 @@ public:
|
||||
|
||||
void SetTextWidgetToCheck(BTextWidget*, BTextWidget* = NULL);
|
||||
|
||||
BTextWidget* ActiveTextWidget() { return fActiveTextWidget; };
|
||||
void SetActiveTextWidget(BTextWidget* w) { fActiveTextWidget = w; };
|
||||
|
||||
protected:
|
||||
// view setup
|
||||
virtual void SetUpDefaultColumnsIfNeeded();
|
||||
@ -808,6 +811,7 @@ protected:
|
||||
static OffscreenBitmap* sOffscreen;
|
||||
|
||||
BTextWidget* fTextWidgetToCheck;
|
||||
BTextWidget* fActiveTextWidget;
|
||||
|
||||
typedef BView _inherited;
|
||||
};
|
||||
|
@ -40,6 +40,7 @@ All rights reserved.
|
||||
|
||||
#include <Alert.h>
|
||||
#include <Catalog.h>
|
||||
#include <Clipboard.h>
|
||||
#include <Debug.h>
|
||||
#include <Directory.h>
|
||||
#include <MessageFilter.h>
|
||||
@ -75,6 +76,7 @@ BTextWidget::BTextWidget(Model* model, BColumn* column, BPoseView* view)
|
||||
fVisible(true),
|
||||
fActive(false),
|
||||
fSymLink(model->IsSymLink()),
|
||||
fMaxWidth(0),
|
||||
fLastClickedTime(0)
|
||||
{
|
||||
}
|
||||
@ -146,27 +148,31 @@ BTextWidget::CalcRectCommon(BPoint poseLoc, const BColumn* column,
|
||||
const BPoseView* view, float textWidth)
|
||||
{
|
||||
BRect result;
|
||||
float viewWidth = textWidth;
|
||||
|
||||
if (view->ViewMode() == kListMode) {
|
||||
viewWidth = std::min(column->Width(), textWidth);
|
||||
|
||||
poseLoc.x += column->Offset();
|
||||
|
||||
switch (fAlignment) {
|
||||
case B_ALIGN_LEFT:
|
||||
result.left = poseLoc.x;
|
||||
result.right = result.left + textWidth + 1;
|
||||
result.right = result.left + 1 + viewWidth;
|
||||
break;
|
||||
|
||||
case B_ALIGN_CENTER:
|
||||
result.left = poseLoc.x + (column->Width() / 2)
|
||||
- (textWidth / 2);
|
||||
result.left = poseLoc.x
|
||||
+ roundf((column->Width() - viewWidth) / 2);
|
||||
if (result.left < 0)
|
||||
result.left = 0;
|
||||
|
||||
result.right = result.left + textWidth + 1;
|
||||
result.right = result.left + 1 + viewWidth;
|
||||
break;
|
||||
|
||||
case B_ALIGN_RIGHT:
|
||||
result.right = poseLoc.x + column->Width();
|
||||
result.left = result.right - textWidth - 1;
|
||||
result.left = result.right - 1 - viewWidth;
|
||||
if (result.left < 0)
|
||||
result.left = 0;
|
||||
break;
|
||||
@ -179,17 +185,22 @@ BTextWidget::CalcRectCommon(BPoint poseLoc, const BColumn* column,
|
||||
result.bottom = poseLoc.y
|
||||
+ roundf((view->ListElemHeight() + view->FontHeight()) / 2);
|
||||
} else {
|
||||
viewWidth = std::min(view->StringWidth("M") * 30, textWidth);
|
||||
if (view->ViewMode() == kIconMode) {
|
||||
// large/scaled icon mode
|
||||
result.left = poseLoc.x + (view->IconSizeInt() - textWidth) / 2;
|
||||
// icon mode
|
||||
result.left = poseLoc.x
|
||||
+ roundf((view->IconSizeInt() - viewWidth) / 2);
|
||||
result.bottom = poseLoc.y + view->IconPoseHeight();
|
||||
} else {
|
||||
// mini icon mode
|
||||
result.left = poseLoc.x + B_MINI_ICON + kMiniIconSeparator;
|
||||
result.bottom = poseLoc.y
|
||||
+ roundf((B_MINI_ICON + view->FontHeight()) / 2);
|
||||
}
|
||||
|
||||
result.right = result.left + textWidth;
|
||||
result.bottom = poseLoc.y + view->IconPoseHeight();
|
||||
result.right = result.left + viewWidth;
|
||||
}
|
||||
|
||||
result.top = result.bottom - view->FontHeight();
|
||||
|
||||
return result;
|
||||
@ -294,7 +305,7 @@ BTextWidget::MouseUp(BRect bounds, BPoseView* view, BPose* pose, BPoint)
|
||||
|
||||
|
||||
static filter_result
|
||||
TextViewFilter(BMessage* message, BHandler**, BMessageFilter* filter)
|
||||
TextViewKeyDownFilter(BMessage* message, BHandler**, BMessageFilter* filter)
|
||||
{
|
||||
uchar key;
|
||||
if (message->FindInt8("byte", (int8*)&key) != B_OK)
|
||||
@ -306,20 +317,20 @@ TextViewFilter(BMessage* message, BHandler**, BMessageFilter* filter)
|
||||
filter->Looper());
|
||||
ThrowOnAssert(window != NULL);
|
||||
|
||||
BPoseView* poseView = window->PoseView();
|
||||
ThrowOnAssert(poseView != NULL);
|
||||
BPoseView* view = window->PoseView();
|
||||
ThrowOnAssert(view != NULL);
|
||||
|
||||
if (key == B_RETURN || key == B_ESCAPE) {
|
||||
poseView->CommitActivePose(key == B_RETURN);
|
||||
view->CommitActivePose(key == B_RETURN);
|
||||
return B_SKIP_MESSAGE;
|
||||
}
|
||||
|
||||
if (key == B_TAB) {
|
||||
if (poseView->ActivePose()) {
|
||||
if (view->ActivePose()) {
|
||||
if (message->FindInt32("modifiers") & B_SHIFT_KEY)
|
||||
poseView->ActivePose()->EditPreviousWidget(poseView);
|
||||
view->ActivePose()->EditPreviousWidget(view);
|
||||
else
|
||||
poseView->ActivePose()->EditNextWidget(poseView);
|
||||
view->ActivePose()->EditNextWidget(view);
|
||||
}
|
||||
|
||||
return B_SKIP_MESSAGE;
|
||||
@ -329,23 +340,94 @@ TextViewFilter(BMessage* message, BHandler**, BMessageFilter* filter)
|
||||
// we try to work-around this "bug" here.
|
||||
|
||||
// find the text editing view
|
||||
BView* scrollView = poseView->FindView("BorderView");
|
||||
BView* scrollView = view->FindView("BorderView");
|
||||
if (scrollView != NULL) {
|
||||
BTextView* textView = dynamic_cast<BTextView*>(
|
||||
scrollView->FindView("WidgetTextView"));
|
||||
if (textView != NULL) {
|
||||
BRect textRect = textView->TextRect();
|
||||
BRect rect = scrollView->Frame();
|
||||
ASSERT(view->ActiveTextWidget() != NULL);
|
||||
float maxWidth = view->ActiveTextWidget()->MaxWidth();
|
||||
bool tooWide = textView->TextRect().Width() > maxWidth;
|
||||
textView->MakeResizable(!tooWide, tooWide ? NULL : scrollView);
|
||||
}
|
||||
}
|
||||
|
||||
if (rect.right + 5 > poseView->Bounds().right
|
||||
|| rect.left - 5 < 0)
|
||||
textView->MakeResizable(true, NULL);
|
||||
return B_DISPATCH_MESSAGE;
|
||||
}
|
||||
|
||||
if (textRect.Width() + 10 < rect.Width()) {
|
||||
textView->MakeResizable(true, scrollView);
|
||||
// make sure no empty white space stays on the right
|
||||
textView->ScrollToOffset(0);
|
||||
|
||||
static filter_result
|
||||
TextViewPasteFilter(BMessage* message, BHandler**, BMessageFilter* filter)
|
||||
{
|
||||
ThrowOnAssert(filter != NULL);
|
||||
|
||||
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
|
||||
filter->Looper());
|
||||
ThrowOnAssert(window != NULL);
|
||||
|
||||
BPoseView* view = window->PoseView();
|
||||
ThrowOnAssert(view != NULL);
|
||||
|
||||
// the BTextView doesn't respect window borders when resizing itself;
|
||||
// we try to work-around this "bug" here.
|
||||
|
||||
// find the text editing view
|
||||
BView* scrollView = view->FindView("BorderView");
|
||||
if (scrollView != NULL) {
|
||||
BTextView* textView = dynamic_cast<BTextView*>(
|
||||
scrollView->FindView("WidgetTextView"));
|
||||
if (textView != NULL) {
|
||||
float textWidth = textView->TextRect().Width();
|
||||
|
||||
// subtract out selected text region width
|
||||
int32 start, finish;
|
||||
textView->GetSelection(&start, &finish);
|
||||
if (start != finish) {
|
||||
BRegion selectedRegion;
|
||||
textView->GetTextRegion(start, finish, &selectedRegion);
|
||||
textWidth -= selectedRegion.Frame().Width();
|
||||
}
|
||||
|
||||
// add pasted text width
|
||||
if (be_clipboard->Lock()) {
|
||||
BMessage* clip = be_clipboard->Data();
|
||||
if (clip != NULL) {
|
||||
const char* text = NULL;
|
||||
ssize_t length = 0;
|
||||
|
||||
if (clip->FindData("text/plain", B_MIME_TYPE,
|
||||
(const void**)&text, &length) == B_OK) {
|
||||
textWidth += textView->StringWidth(text);
|
||||
}
|
||||
}
|
||||
|
||||
be_clipboard->Unlock();
|
||||
}
|
||||
|
||||
// check if pasted text is too wide
|
||||
ASSERT(view->ActiveTextWidget() != NULL);
|
||||
float maxWidth = view->ActiveTextWidget()->MaxWidth();
|
||||
bool tooWide = textWidth > maxWidth;
|
||||
|
||||
if (tooWide) {
|
||||
// resize text view to max width
|
||||
|
||||
// move scroll view if not left aligned
|
||||
float oldWidth = textView->Bounds().Width();
|
||||
float newWidth = maxWidth;
|
||||
float right = oldWidth - newWidth;
|
||||
|
||||
if (textView->Alignment() == B_ALIGN_CENTER)
|
||||
scrollView->MoveBy(roundf(right / 2), 0);
|
||||
else if (textView->Alignment() == B_ALIGN_RIGHT)
|
||||
scrollView->MoveBy(right, 0);
|
||||
|
||||
// resize scroll view
|
||||
float grow = newWidth - oldWidth;
|
||||
scrollView->ResizeBy(grow, 0);
|
||||
}
|
||||
|
||||
textView->MakeResizable(!tooWide, tooWide ? NULL : scrollView);
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,18 +448,13 @@ BTextWidget::StartEdit(BRect bounds, BPoseView* view, BPose* pose)
|
||||
return;
|
||||
}
|
||||
|
||||
view->SetActiveTextWidget(this);
|
||||
|
||||
// TODO fix text rect being off by a pixel on some files
|
||||
|
||||
// get bounds with full text length
|
||||
BRect rect(bounds);
|
||||
BRect textRect(bounds);
|
||||
|
||||
// label offset
|
||||
float hOffset = 0;
|
||||
float vOffset = view->ViewMode() == kListMode ? -1 : -2;
|
||||
rect.OffsetBy(hOffset, vOffset);
|
||||
|
||||
BTextView* textView = new BTextView(rect, "WidgetTextView", textRect,
|
||||
rect.OffsetBy(view->ViewMode() == kListMode ? -1 : 1, -4);
|
||||
BTextView* textView = new BTextView(rect, "WidgetTextView", rect,
|
||||
be_plain_font, 0, B_FOLLOW_ALL, B_WILL_DRAW);
|
||||
|
||||
textView->SetWordWrap(false);
|
||||
@ -385,36 +462,34 @@ BTextWidget::StartEdit(BRect bounds, BPoseView* view, BPose* pose)
|
||||
DisallowMetaKeys(textView);
|
||||
fText->SetUpEditing(textView);
|
||||
|
||||
textView->AddFilter(new BMessageFilter(B_KEY_DOWN, TextViewFilter));
|
||||
textView->AddFilter(new BMessageFilter(B_KEY_DOWN, TextViewKeyDownFilter));
|
||||
textView->AddFilter(new BMessageFilter(B_PASTE, TextViewPasteFilter));
|
||||
|
||||
// get full text length
|
||||
rect.right = rect.left + textView->LineWidth();
|
||||
rect.bottom = rect.top + textView->LineHeight() - 1;
|
||||
rect.bottom = rect.top + textView->LineHeight() - 1 + 4;
|
||||
|
||||
// enlarge rect by inset amount
|
||||
rect.InsetBy(-2, -2);
|
||||
if (view->ViewMode() == kListMode) {
|
||||
// limit max width to column width in list mode
|
||||
BColumn* column = view->ColumnFor(fAttrHash);
|
||||
ASSERT(column != NULL);
|
||||
fMaxWidth = column->Width();
|
||||
} else {
|
||||
// limit max width to 30em in icon and mini icon mode
|
||||
fMaxWidth = textView->StringWidth("M") * 30;
|
||||
|
||||
// undo label offset
|
||||
textRect = rect.OffsetToCopy(-hOffset, -vOffset);
|
||||
|
||||
textView->SetTextRect(textRect);
|
||||
|
||||
BPoint origin = view->LeftTop();
|
||||
textRect = view->Bounds();
|
||||
|
||||
bool hitBorder = false;
|
||||
if (rect.left <= origin.x)
|
||||
rect.left = origin.x + 1, hitBorder = true;
|
||||
if (rect.right >= textRect.right)
|
||||
rect.right = textRect.right - 1, hitBorder = true;
|
||||
if (textView->LineWidth() > fMaxWidth) {
|
||||
// compensate for text going over right inset
|
||||
rect.OffsetBy(-2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// resize textView
|
||||
textView->MoveTo(rect.LeftTop());
|
||||
textView->ResizeTo(rect.Width(), rect.Height());
|
||||
textView->ResizeTo(std::min(fMaxWidth, rect.Width()), rect.Height());
|
||||
textView->SetTextRect(rect);
|
||||
|
||||
BScrollView* scrollView = new BScrollView("BorderView", textView, 0, 0,
|
||||
false, false, B_PLAIN_BORDER);
|
||||
view->AddChild(scrollView);
|
||||
|
||||
// configure text view
|
||||
// set alignment before adding textView so it doesn't redraw
|
||||
switch (view->ViewMode()) {
|
||||
case kIconMode:
|
||||
textView->SetAlignment(B_ALIGN_CENTER);
|
||||
@ -428,7 +503,13 @@ BTextWidget::StartEdit(BRect bounds, BPoseView* view, BPose* pose)
|
||||
textView->SetAlignment(fAlignment);
|
||||
break;
|
||||
}
|
||||
textView->MakeResizable(true, hitBorder ? NULL : scrollView);
|
||||
|
||||
BScrollView* scrollView = new BScrollView("BorderView", textView, 0, 0,
|
||||
false, false, B_PLAIN_BORDER);
|
||||
view->AddChild(scrollView);
|
||||
|
||||
bool tooWide = textView->TextRect().Width() > fMaxWidth;
|
||||
textView->MakeResizable(!tooWide, tooWide ? NULL : scrollView);
|
||||
|
||||
view->SetActivePose(pose);
|
||||
// tell view about pose
|
||||
@ -438,8 +519,6 @@ BTextWidget::StartEdit(BRect bounds, BPoseView* view, BPose* pose)
|
||||
textView->SelectAll();
|
||||
textView->ScrollToSelection();
|
||||
// scroll to beginning so that text is visible
|
||||
textView->ScrollBy(-1, -2);
|
||||
// scroll in rect to center text
|
||||
textView->MakeFocus();
|
||||
|
||||
// make this text widget invisible while we edit it
|
||||
@ -459,6 +538,8 @@ void
|
||||
BTextWidget::StopEdit(bool saveChanges, BPoint poseLoc, BPoseView* view,
|
||||
BPose* pose, int32 poseIndex)
|
||||
{
|
||||
view->SetActiveTextWidget(NULL);
|
||||
|
||||
// find the text editing view
|
||||
BView* scrollView = view->FindView("BorderView");
|
||||
ASSERT(scrollView != NULL);
|
||||
|
@ -107,6 +107,9 @@ public:
|
||||
void CheckExpiration();
|
||||
void CancelWait();
|
||||
|
||||
float MaxWidth() { return fMaxWidth; };
|
||||
void SetMaxWidth(float maxWidth) { fMaxWidth = maxWidth; };
|
||||
|
||||
private:
|
||||
BRect CalcRectCommon(BPoint poseLoc, const BColumn*, const BPoseView*,
|
||||
float width);
|
||||
@ -121,6 +124,8 @@ private:
|
||||
bool fActive : 1;
|
||||
bool fSymLink : 1;
|
||||
|
||||
float fMaxWidth;
|
||||
|
||||
bigtime_t fLastClickedTime;
|
||||
struct MouseUpParams fParams;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user