diff --git a/headers/os/interface/TextView.h b/headers/os/interface/TextView.h index 3757bb1862..951948a092 100644 --- a/headers/os/interface/TextView.h +++ b/headers/os/interface/TextView.h @@ -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); diff --git a/src/kits/interface/TextView.cpp b/src/kits/interface/TextView.cpp index 695688c411..f214d2c42f 100644 --- a/src/kits/interface/TextView.cpp +++ b/src/kits/interface/TextView.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -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); - } } diff --git a/src/kits/tracker/ContainerWindow.cpp b/src/kits/tracker/ContainerWindow.cpp index 8622edec0b..62c2fd9cc8 100644 --- a/src/kits/tracker/ContainerWindow.cpp +++ b/src/kits/tracker/ContainerWindow.cpp @@ -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; diff --git a/src/kits/tracker/PoseView.cpp b/src/kits/tracker/PoseView.cpp index 6eebdaf178..2e2b6179a5 100644 --- a/src/kits/tracker/PoseView.cpp +++ b/src/kits/tracker/PoseView.cpp @@ -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); diff --git a/src/kits/tracker/PoseView.h b/src/kits/tracker/PoseView.h index 366c1f5716..92707cc892 100644 --- a/src/kits/tracker/PoseView.h +++ b/src/kits/tracker/PoseView.h @@ -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; }; diff --git a/src/kits/tracker/TextWidget.cpp b/src/kits/tracker/TextWidget.cpp index 0b5f6c1551..fdf7ac5f01 100644 --- a/src/kits/tracker/TextWidget.cpp +++ b/src/kits/tracker/TextWidget.cpp @@ -40,6 +40,7 @@ All rights reserved. #include #include +#include #include #include #include @@ -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( 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( + 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( + 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); diff --git a/src/kits/tracker/TextWidget.h b/src/kits/tracker/TextWidget.h index b1c3134a61..50ce076088 100644 --- a/src/kits/tracker/TextWidget.h +++ b/src/kits/tracker/TextWidget.h @@ -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; };