IK: align BTextView text rect/fix alignment

Recalculate line breaks in FrameResized() if word-wrap is on, otherwise
only move the text rect into position. StyledEdit was recalculating line
breaks before on resize (we have to in this mode) and the frame offset
updates for non-wrapping text views are inexpensive. This makes resizing
text view's work like StyledEdit everywhere.

Scroll to cursor when word-wrap setting changes if text view is editable.
If you are viewing a long document changing word-wrap can move the cursor
quite far, so scroll back to it.

Fix _ActualTabWidth() pen location for right and center-aligned text views
so that tabs widths are calculated correctly.

Reset fTextRect horizontal limits to bounds minus insets in
_RecalculateLineBreaks(), then grow fTextRect based on alignment when
wrap is off.

Fixing insets also fixes right and center-aligned BTextViews.
Left-aligned text view's grow right, right-aligned ones grow left,
and center-aligned ones grow out.

Make extra scrolling space for all aligned text views go the other way
from how it did in hrev24130 (and on left-aligned text view's too) so
that half the text is visible when you edit past the end or before the
beginnning of a text view instead of none of it.

Fixes #1651 #12608 #13796 #15189

Do not _RecalculateLineBreaks() if text view bounds are invalid.
In SetText() detect invalid text view bounds and resize the view to the
width and height of the first line. Then recalculate line breaks.
This fixes BAlert text view size issues.

Fixes #16481 (regression from hrev54496.)

Remove useless and heavy computation. There is no point in computing line
breaks for a 10px wide text view and it takes a long time because it needs
a lot of linebreaks. The view eventually gets laid out properly.

Fixes #5582 (which was not locale-related, after all.)

Only apply default insets if text rect is set to bounds. This ensures
that apps that manipulate the text rect can continue to do so without
the default insets interfering while apps that don't can benefit
from the defaults. If you want to set the text rect to bounds and
not use the default insets you must override the default by calling
SetInsets(). This prevent the default insets from being applied once
apps have changed the text rect fixing a bug in Icon-O-Matic where the
text rect insets were being applied incorrectly.

Fixes #16488 (regression from hrev54496.)

Reduce left and right insets inside text views from full label spacing
to half label spacing. Unify padding between BTextControl and BTextView.

Move fLayoutData->UpdateInsets() to private BTextView::_UpdateInsets()
because we need access to BTextView member variables when deciding
whether or not to add the default padding or not.

_UpdateInsets() changes:
* Don't update insets if BTextView::SetInsets() was called.
* Don't add default insets unless fTextRect is set to view Bounds().
* Do not set the right and bottom insets to left and top if negative,
  set them to 0 like we do to left and top -- DeskCalc bug otherwise.

Fixes #15688

Other BTextView fixes:
* Replace max_c and min_c with std::max and std::min respectively.
* Remove scrolling from one instance of BTextView::SetText() as it
  produced undesired results while editing a scrolled text view.
* Add default insets in _UpdateInsets()
* Fix scrolling when entering and deleting text so that some part of
  the text is always visible. Make visible scroll width depend on font
  size.
* Allow scrolling to a negative offset in x but not y. This allows you
  to scroll the entire contents of right and centered-aligned text views
  whose content does not fit in the box.
* Change _Refresh() to take an offset instead of a bool so that you can
  scroll to any offset.
* Replace TextLength() with fText->Length() in a couple of places.

TextControl changes:
* Set text rect in BTextControl::DoLayout().
* Remove AlignTextRect() from TextInput.

Fix the following problems in apps:

ScreenSaver: Set text rect in PreviewView::AddPreview().
Tracker: Set "Edit name" text view insets to 2. Tweek text rect position
  to be on top of label in icon, mini-icon, and list mode. Add a TODO that
  the text rect is a pixel off from the name on some files.
Mail: Remove _AlignTextRect() and FrameResized() from AddressTextControl.
  Use default insets on the text view, defaults are fine here.
DeskCalc: Set insets based on font size in ExpressionTextView
  SetTextRect() instead of manipulating the text rect.
  Remove _CheckTextRect() and related methods from InputTextView.
Icon-O-Matic: Remove _CheckTextRect() and related methods from InputTextView.
WebPositive: Remove _AlignTextRect() and FrameResized() from URLTextView
  and call SetInsets().
StyledEdit: Word-wrap and FrameResized() changes ported to BTextView.

Fixes #16476 #16480 #16488 (regressions from hrev54496.)

Change-Id: Ifeca6077f8815ccd86d5a3880f99556298aaf0fe
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3152
Reviewed-by: John Scipione <jscipione@gmail.com>
This commit is contained in:
John Scipione 2020-07-20 11:00:41 -04:00
parent 3d43a90508
commit d9385a9d38
19 changed files with 348 additions and 410 deletions

View File

@ -1,11 +1,13 @@
/*
* Copyright 2007-2009, Haiku, Inc. All rights reserved.
* Copyright 2007-2020 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _TEXTVIEW_H
#define _TEXTVIEW_H
#include <stdint.h>
#include <Locker.h>
#include <View.h>
@ -297,7 +299,7 @@ private:
int32 numBytes);
void _Refresh(int32 fromOffset, int32 toOffset,
bool scroll);
int32 scrollTo = INT32_MIN);
void _RecalculateLineBreaks(int32* startLine,
int32* endLine);
int32 _FindLineBreak(int32 fromOffset,
@ -411,6 +413,8 @@ private:
void _FilterDisallowedChars(char* text,
ssize_t& length, text_run_array* runArray);
void _UpdateInsets(const BRect& rect);
private:
BPrivate::TextGapBuffer* fText;
LineBuffer* fLines;

View File

@ -763,8 +763,6 @@ CalcView::FrameResized(float width, float height)
fExpressionTextView->SetFontAndColor(&font, B_FONT_ALL, &fontColor);
expressionRect.OffsetTo(B_ORIGIN);
float inset = (expressionRect.Height() - fExpressionTextView->LineHeight(0)) / 2;
expressionRect.InsetBy(0, inset);
fExpressionTextView->SetTextRect(expressionRect);
Invalidate();
}

View File

@ -14,6 +14,7 @@
#include <stdio.h>
#include <Beep.h>
#include <ControlLook.h>
#include <Window.h>
#include "CalcView.h"
@ -134,6 +135,9 @@ ExpressionTextView::GetDragParameters(BMessage* dragMessage,
void
ExpressionTextView::SetTextRect(BRect rect)
{
float hInset = floorf(be_control_look->DefaultLabelSpacing() / 2);
float vInset = floorf((rect.Height() - LineHeight(0)) / 2);
InputTextView::SetInsets(hInset, vInset, hInset, vInset);
InputTextView::SetTextRect(rect);
int32 count = fPreviousExpressions.CountItems();
@ -199,7 +203,8 @@ ExpressionTextView::SetValue(BString value)
float stringWidth = font.StringWidth(value);
// make the string shorter if it does not fit in the view
float viewWidth = Frame().Width();
float viewWidth = Frame().Width()
- floorf(be_control_look->DefaultLabelSpacing() / 2);
if (value.CountChars() > 3 && stringWidth > viewWidth) {
// get the position of the first digit
int32 firstDigit = 0;

View File

@ -33,7 +33,7 @@ class ExpressionTextView : public InputTextView {
virtual void GetDragParameters(BMessage* dragMessage,
BBitmap** bitmap, BPoint* point,
BHandler** handler);
void SetTextRect(BRect rect);
virtual void SetTextRect(BRect rect);
// InputTextView
virtual void RevertChanges();

View File

@ -113,52 +113,3 @@ InputTextView::Invoke(BMessage* message)
}
return B_BAD_VALUE;
}
// #pragma mark -
void
InputTextView::Select(int32 start, int32 finish)
{
BTextView::Select(start, finish);
_CheckTextRect();
}
void
InputTextView::InsertText(const char* inText, int32 inLength, int32 inOffset,
const text_run_array* inRuns)
{
BTextView::InsertText(inText, inLength, inOffset, inRuns);
_CheckTextRect();
}
void
InputTextView::DeleteText(int32 fromOffset, int32 toOffset)
{
BTextView::DeleteText(fromOffset, toOffset);
_CheckTextRect();
}
// #pragma mark -
void
InputTextView::_CheckTextRect()
{
// update text rect and make sure
// the cursor/selection is in view
BRect textRect(TextRect());
float width = ceilf(StringWidth(Text()) + 2.0);
if (textRect.Width() < width) {
textRect.right = textRect.left + width;
SetTextRect(textRect);
ScrollToSelection();
}
}

View File

@ -37,18 +37,6 @@ class InputTextView : public BTextView, public BInvoker {
virtual void ApplyChanges() = 0;
protected:
// BTextView
virtual void Select(int32 start, int32 finish);
virtual void InsertText(const char* inText,
int32 inLength,
int32 inOffset,
const text_run_array* inRuns);
virtual void DeleteText(int32 fromOffset,
int32 toOffset);
void _CheckTextRect();
bool fWasFocus;
};

View File

@ -16,7 +16,8 @@ InputTextView::InputTextView(BRect frame, const char* name,
uint32 resizingMode,
uint32 flags)
: BTextView(frame, name, textRect, resizingMode, flags),
fWasFocus(false)
fWasFocus(false),
fTextBeforeFocus("")
{
SetWordWrap(false);
}
@ -117,50 +118,3 @@ InputTextView::Invoke(BMessage* message)
}
return B_BAD_VALUE;
}
// #pragma mark -
// Select
void
InputTextView::Select(int32 start, int32 finish)
{
BTextView::Select(start, finish);
_CheckTextRect();
}
// InsertText
void
InputTextView::InsertText(const char* inText, int32 inLength, int32 inOffset,
const text_run_array* inRuns)
{
BTextView::InsertText(inText, inLength, inOffset, inRuns);
_CheckTextRect();
}
// DeleteText
void
InputTextView::DeleteText(int32 fromOffset, int32 toOffset)
{
BTextView::DeleteText(fromOffset, toOffset);
_CheckTextRect();
}
// #pragma mark -
// _CheckTextRect
void
InputTextView::_CheckTextRect()
{
// update text rect and make sure
// the cursor/selection is in view
BRect textRect(TextRect());
float width = ceilf(StringWidth(Text()) + 2.0);
if (textRect.Width() != width) {
textRect.right = textRect.left + width;
SetTextRect(textRect);
ScrollToSelection();
}
}

View File

@ -34,21 +34,8 @@ class InputTextView : public BTextView,
virtual void ApplyChanges() = 0;
protected:
// BTextView
virtual void Select(int32 start, int32 finish);
virtual void InsertText(const char* inText,
int32 inLength,
int32 inOffset,
const text_run_array* inRuns);
virtual void DeleteText(int32 fromOffset,
int32 toOffset);
void _CheckTextRect();
bool fWasFocus;
BString fTextBeforeFocus;
};
#endif // INPUT_TEXT_VIEW_H

View File

@ -48,7 +48,6 @@ public:
virtual ~TextView();
virtual void MessageReceived(BMessage* message);
virtual void FrameResized(float width, float height);
virtual void KeyDown(const char* bytes, int32 numBytes);
virtual void MakeFocus(bool focused = true);
@ -66,9 +65,6 @@ protected:
const text_run_array* runs);
virtual void DeleteText(int32 fromOffset, int32 toOffset);
private:
void _AlignTextRect();
private:
AddressTextControl* fAddressTextControl;
TextViewCompleter* fAutoCompleter;
@ -221,14 +217,6 @@ AddressTextControl::TextView::MessageReceived(BMessage* message)
}
void
AddressTextControl::TextView::FrameResized(float width, float height)
{
BTextView::FrameResized(width, height);
_AlignTextRect();
}
void
AddressTextControl::TextView::KeyDown(const char* bytes, int32 numBytes)
{
@ -383,25 +371,6 @@ AddressTextControl::TextView::DeleteText(int32 fromOffset,
}
void
AddressTextControl::TextView::_AlignTextRect()
{
// Layout the text rect to be in the middle, normally this means there
// is one pixel spacing on each side.
BRect textRect(Bounds());
textRect.left = 0.0;
float vInset = max_c(1,
floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
float hInset = 0;
if (be_control_look != NULL)
hInset = be_control_look->DefaultLabelSpacing();
textRect.InsetBy(hInset, vInset);
SetTextRect(textRect);
}
// #pragma mark - PopUpButton
@ -888,6 +857,7 @@ void
AddressTextControl::SetEditable(bool editable)
{
fTextView->MakeEditable(IsEnabled() && editable);
fTextView->MakeSelectable(IsEnabled() && editable);
fEditable = editable;
if (editable && fPopUpButton->IsHidden(this))

View File

@ -51,19 +51,10 @@ StyledEditView::~StyledEditView()
}
void
StyledEditView::FrameResized(float width, float height)
{
BTextView::FrameResized(width, height);
if (DoesWordWrap()) {
BRect textRect;
textRect = Bounds();
textRect.OffsetTo(B_ORIGIN);
textRect.InsetBy(TEXT_INSET, TEXT_INSET);
SetTextRect(textRect);
}
}

View File

@ -502,20 +502,16 @@ StyledEditWindow::MessageReceived(BMessage* message)
break;
case WRAP_LINES:
{
BRect textRect(fTextView->Bounds());
textRect.OffsetTo(B_ORIGIN);
textRect.InsetBy(TEXT_INSET, TEXT_INSET);
// update wrap setting
if (fTextView->DoesWordWrap()) {
fTextView->SetWordWrap(false);
fWrapItem->SetMarked(false);
// the width comes from stylededit R5. TODO: find a better way
textRect.SetRightBottom(BPoint(1500.0, textRect.RightBottom().y));
} else {
fTextView->SetWordWrap(true);
fWrapItem->SetMarked(true);
}
fTextView->SetTextRect(textRect);
// update buttons
_UpdateCleanUndoRedoSaveRevert();
break;
}

View File

@ -147,7 +147,6 @@ public:
virtual ~URLTextView();
virtual void MessageReceived(BMessage* message);
virtual void FrameResized(float width, float height);
virtual void MouseDown(BPoint where);
virtual void KeyDown(const char* bytes, int32 numBytes);
virtual void MakeFocus(bool focused = true);
@ -163,9 +162,6 @@ protected:
const text_run_array* inRuns);
virtual void DeleteText(int32 fromOffset, int32 toOffset);
private:
void _AlignTextRect();
private:
URLInputGroup* fURLInputGroup;
TextViewCompleter* fURLAutoCompleter;
@ -183,6 +179,7 @@ URLInputGroup::URLTextView::URLTextView(URLInputGroup* parent)
{
MakeResizable(true);
SetStylable(true);
SetInsets(be_control_look->DefaultLabelSpacing(), 2, 0, 2);
fURLAutoCompleter->SetModificationsReported(true);
}
@ -208,14 +205,6 @@ URLInputGroup::URLTextView::MessageReceived(BMessage* message)
}
void
URLInputGroup::URLTextView::FrameResized(float width, float height)
{
BTextView::FrameResized(width, height);
_AlignTextRect();
}
void
URLInputGroup::URLTextView::MouseDown(BPoint where)
{
@ -411,25 +400,6 @@ URLInputGroup::URLTextView::DeleteText(int32 fromOffset, int32 toOffset)
}
void
URLInputGroup::URLTextView::_AlignTextRect()
{
// Layout the text rect to be in the middle, normally this means there
// is one pixel spacing on each side.
BRect textRect(Bounds());
textRect.left = 0.0;
float vInset = max_c(1,
floorf((textRect.Height() - LineHeight(0)) / 2.0 + 0.5));
float hInset = kHorizontalTextRectInset;
if (be_control_look)
hInset = be_control_look->DefaultLabelSpacing();
textRect.InsetBy(hInset, vInset);
SetTextRect(textRect);
}
const uint32 kGoBitmapWidth = 14;
const uint32 kGoBitmapHeight = 14;
const color_space kGoBitmapFormat = B_RGBA32;

View File

@ -1,11 +1,12 @@
/*
* Copyright 2001-2015, Haiku Inc.
* Copyright 2001-2020 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Frans van Nispen (xlr8@tref.nl)
* Stephan Aßmus <superstippi@gmx.de>
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
* John Scipione <jscipione@gmail.com>
*/
@ -637,7 +638,6 @@ void
BTextControl::SetAlignment(alignment labelAlignment, alignment textAlignment)
{
fText->SetAlignment(textAlignment);
fText->AlignTextRect();
if (fLabelAlign != labelAlignment) {
fLabelAlign = labelAlignment;
@ -911,6 +911,7 @@ BTextControl::DoLayout()
// place the text view and set the divider
textFrame.InsetBy(kFrameMargin, kFrameMargin);
BLayoutUtils::AlignInFrame(fText, textFrame);
fText->SetTextRect(textFrame.OffsetToCopy(B_ORIGIN));
fDivider = divider;
@ -1115,7 +1116,6 @@ BTextControl::_InitText(const char* initialText, const BMessage* archive)
SetText(initialText);
fText->SetAlignment(B_ALIGN_LEFT);
fText->AlignTextRect();
}
// Although this is not strictly initializing the text view,
@ -1171,7 +1171,6 @@ BTextControl::_LayoutTextView()
frame.InsetBy(kFrameMargin, kFrameMargin);
fText->MoveTo(frame.left, frame.top);
fText->ResizeTo(frame.Width(), frame.Height());
fText->AlignTextRect();
TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
TRACE("fDivider: %.2f\n", fDivider);

View File

@ -1,10 +1,11 @@
/*
* Copyright 2001-2015, Haiku Inc. All rights reserved.
* Copyright 2001-2020 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Frans van Nispen (xlr8@tref.nl)
* Marc Flerackers (mflerackers@androme.be)
* John Scipione (jscipione@gmail.com)
*/
@ -14,7 +15,6 @@
#include <stdlib.h>
#include <string.h>
#include <ControlLook.h>
#include <InterfaceDefs.h>
#include <LayoutUtils.h>
#include <Message.h>
@ -84,8 +84,6 @@ void
_BTextInput_::FrameResized(float width, float height)
{
BTextView::FrameResized(width, height);
AlignTextRect();
}
@ -162,42 +160,6 @@ _BTextInput_::MinSize()
}
void
_BTextInput_::AlignTextRect()
{
// the label font could require the control to be higher than
// necessary for the text view, we compensate this by layouting
// the text rect to be in the middle, normally this means there
// is one pixel spacing on each side
BRect textRect(Bounds());
float vInset = max_c(1,
floorf((textRect.Height() - LineHeight(0)) / 2.0));
float hInset = 2;
float textFontWidth = TextRect().right;
switch (Alignment()) {
case B_ALIGN_LEFT:
hInset = be_control_look->DefaultLabelSpacing();
break;
case B_ALIGN_RIGHT:
hInset = textRect.right - textFontWidth;
hInset -= be_control_look->DefaultLabelSpacing();
break;
case B_ALIGN_CENTER:
hInset = (textRect.right - textFontWidth) / 2.0;
break;
default:
break;
}
textRect.InsetBy(hInset, vInset);
SetTextRect(textRect);
}
void
_BTextInput_::SetInitialText()
{

View File

@ -1,5 +1,5 @@
/*
* Copyright 2001-2008, Haiku Inc. All rights reserved.
* Copyright 2001-2020 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@ -11,6 +11,7 @@
#ifndef _TEXT_CONTROLI_H
#define _TEXT_CONTROLI_H
#include <TextView.h>
@ -36,7 +37,6 @@ virtual void MakeFocus(bool focusState = true);
virtual BSize MinSize();
void AlignTextRect();
void SetInitialText();
virtual void Paste(BClipboard *clipboard);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2001-2015 Haiku, Inc. All rights reserved.
* Copyright 2001-2020 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@ -25,6 +25,7 @@
#include <TextView.h>
#include <algorithm>
#include <new>
#include <stdio.h>
@ -34,6 +35,7 @@
#include <Beep.h>
#include <Bitmap.h>
#include <Clipboard.h>
#include <ControlLook.h>
#include <Debug.h>
#include <Entry.h>
#include <Input.h>
@ -155,28 +157,11 @@ struct BTextView::LayoutData {
topInset(0),
rightInset(0),
bottomInset(0),
valid(false)
valid(false),
overridden(false)
{
}
void UpdateInsets(const BRect& bounds, const BRect& textRect)
{
// we disallow negative insets, as they would cause parts of the
// text to be hidden
leftInset = textRect.left >= bounds.left
? textRect.left - bounds.left
: 0;
topInset = textRect.top >= bounds.top
? textRect.top - bounds.top
: 0;
rightInset = bounds.right >= textRect.right
? bounds.right - textRect.right
: leftInset;
bottomInset = bounds.bottom >= textRect.bottom
? bounds.bottom - textRect.bottom
: topInset;
}
float leftInset;
float topInset;
float rightInset;
@ -184,7 +169,8 @@ struct BTextView::LayoutData {
BSize min;
BSize preferred;
bool valid;
bool valid : 1;
bool overridden : 1;
};
@ -260,7 +246,15 @@ BTextView::BTextView(BRect frame, const char* name, BRect textRect,
uint32 resizeMask, uint32 flags)
:
BView(frame, name, resizeMask,
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE),
fText(NULL),
fLines(NULL),
fStyles(NULL),
fDisallowedChars(NULL),
fUndo(NULL),
fDragRunner(NULL),
fClickRunner(NULL),
fLayoutData(NULL)
{
_InitObject(textRect, NULL, NULL);
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
@ -272,7 +266,15 @@ BTextView::BTextView(BRect frame, const char* name, BRect textRect,
uint32 resizeMask, uint32 flags)
:
BView(frame, name, resizeMask,
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE),
fText(NULL),
fLines(NULL),
fStyles(NULL),
fDisallowedChars(NULL),
fUndo(NULL),
fDragRunner(NULL),
fClickRunner(NULL),
fLayoutData(NULL)
{
_InitObject(textRect, initialFont, initialColor);
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
@ -282,7 +284,15 @@ BTextView::BTextView(BRect frame, const char* name, BRect textRect,
BTextView::BTextView(const char* name, uint32 flags)
:
BView(name,
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE),
fText(NULL),
fLines(NULL),
fStyles(NULL),
fDisallowedChars(NULL),
fUndo(NULL),
fDragRunner(NULL),
fClickRunner(NULL),
fLayoutData(NULL)
{
_InitObject(Bounds(), NULL, NULL);
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
@ -293,7 +303,15 @@ BTextView::BTextView(const char* name, const BFont* initialFont,
const rgb_color* initialColor, uint32 flags)
:
BView(name,
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE),
fText(NULL),
fLines(NULL),
fStyles(NULL),
fDisallowedChars(NULL),
fUndo(NULL),
fDragRunner(NULL),
fClickRunner(NULL),
fLayoutData(NULL)
{
_InitObject(Bounds(), initialFont, initialColor);
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
@ -302,7 +320,15 @@ BTextView::BTextView(const char* name, const BFont* initialFont,
BTextView::BTextView(BMessage* archive)
:
BView(archive)
BView(archive),
fText(NULL),
fLines(NULL),
fStyles(NULL),
fDisallowedChars(NULL),
fUndo(NULL),
fDragRunner(NULL),
fClickRunner(NULL),
fLayoutData(NULL)
{
CALLED();
BRect rect;
@ -376,7 +402,7 @@ BTextView::BTextView(BMessage* archive)
text_run_array* runArray = UnflattenRunArray(flattenedRun,
(int32*)&runSize);
if (runArray) {
SetRunArray(0, TextLength(), runArray);
SetRunArray(0, fText->Length(), runArray);
FreeRunArray(runArray);
}
}
@ -394,8 +420,8 @@ BTextView::~BTextView()
delete fStyles;
delete fDisallowedChars;
delete fUndo;
delete fClickRunner;
delete fDragRunner;
delete fClickRunner;
delete fLayoutData;
}
@ -451,7 +477,7 @@ BTextView::Archive(BMessage* data, bool deep) const
if (err == B_OK) {
int32 runSize = 0;
text_run_array* runArray = RunArray(0, TextLength());
text_run_array* runArray = RunArray(0, fText->Length());
void* flattened = FlattenRunArray(runArray, &runSize);
if (flattened != NULL) {
@ -774,6 +800,53 @@ void
BTextView::FrameResized(float newWidth, float newHeight)
{
BView::FrameResized(newWidth, newHeight);
if (fWrap) {
// recalculate line breaks
_ResetTextRect();
} else {
// don't recalculate line breaks,
// move text rect into position and redraw.
BRect oldTextRect(fTextRect);
BRect bounds(0, 0, newWidth, newHeight);
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;
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;
}
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);
}
}
_UpdateScrollbars();
}
@ -1103,10 +1176,16 @@ BTextView::SetText(const char* text, int32 length, const text_run_array* runs)
if (text != NULL && length > 0)
InsertText(text, length, 0, runs);
// bounds are invalid, set them based on text
if (!Bounds().IsValid()) {
ResizeTo(LineWidth(0), LineHeight(0));
fTextRect = Bounds();
_UpdateInsets(fTextRect);
}
// recalculate line breaks and draw the text
_Refresh(0, length, false);
_Refresh(0, length);
fCaretOffset = fSelStart = fSelEnd = 0;
ScrollTo(B_ORIGIN);
// draw the caret
_ShowCaret();
@ -1144,7 +1223,7 @@ BTextView::SetText(BFile* file, int32 offset, int32 length,
}
// recalculate line breaks and draw the text
_Refresh(0, length, false);
_Refresh(0, length);
fCaretOffset = fSelStart = fSelEnd = 0;
ScrollToOffset(fSelStart);
@ -1201,6 +1280,7 @@ BTextView::Delete(int32 startOffset, int32 endOffset)
startOffset = 0;
else if (startOffset > fText->Length())
startOffset = fText->Length();
if (endOffset < 0)
endOffset = 0;
else if (endOffset > fText->Length())
@ -1230,7 +1310,7 @@ BTextView::Delete(int32 startOffset, int32 endOffset)
fSelEnd = fSelStart = fCaretOffset;
// recalculate line breaks and draw what's left
_Refresh(startOffset, endOffset, false);
_Refresh(startOffset, endOffset, fCaretOffset);
// draw the caret
_ShowCaret();
@ -1567,7 +1647,7 @@ BTextView::SetFontAndColor(int32 startOffset, int32 endOffset,
// B_SUPPORTS_LAYOUT) and have it _Refresh() automatically?
InvalidateLayout();
// recalc the line breaks and redraw with new style
_Refresh(startOffset, endOffset, false);
_Refresh(startOffset, endOffset);
} else {
// the line breaks wont change, simply redraw
_RequestDrawLines(_LineAt(startOffset), _LineAt(endOffset));
@ -1631,7 +1711,7 @@ BTextView::SetRunArray(int32 startOffset, int32 endOffset,
_SetRunArray(startOffset, endOffset, runs);
_Refresh(startOffset, endOffset, false);
_Refresh(startOffset, endOffset);
}
@ -1740,7 +1820,7 @@ BTextView::PointAt(int32 offset, float* _height) const
float lineWidth = onEmptyLastLine ? 0.0 : LineWidth(lineNum);
float alignmentOffset = fTextRect.Width() - lineWidth;
if (fAlignment == B_ALIGN_CENTER)
alignmentOffset /= 2;
alignmentOffset = floorf(alignmentOffset / 2);
result.x += alignmentOffset;
}
@ -1788,12 +1868,12 @@ BTextView::OffsetAt(BPoint point) const
if (fAlignment != B_ALIGN_LEFT) {
float alignmentOffset = fTextRect.Width() - LineWidth(lineNum);
if (fAlignment == B_ALIGN_CENTER)
alignmentOffset /= 2;
alignmentOffset = floorf(alignmentOffset / 2);
point.x -= alignmentOffset;
}
point.x -= fTextRect.left;
point.x = max_c(point.x, 0.0);
point.x = std::max(point.x, 0.0f);
// ToDo: The following code isn't very efficient, because it always starts
// from the left end, so when the point is near the right end it's very
@ -2041,14 +2121,14 @@ BTextView::GetTextRegion(int32 startOffset, int32 endOffset,
if (startPt.y == endPt.y) {
// this is a one-line region
selRect.left = max_c(startPt.x, fTextRect.left);
selRect.left = std::max(startPt.x, fTextRect.left);
selRect.top = startPt.y;
selRect.right = endPt.x - 1.0;
selRect.bottom = endPt.y + endLineHeight - 1.0;
outRegion->Include(selRect);
} else {
// more than one line in the specified offset range
selRect.left = max_c(startPt.x, fTextRect.left);
selRect.left = std::max(startPt.x, fTextRect.left);
selRect.top = startPt.y;
selRect.right = fTextRect.right;
selRect.bottom = startPt.y + startLineHeight - 1.0;
@ -2082,13 +2162,11 @@ BTextView::ScrollToOffset(int32 offset)
BPoint point = PointAt(offset, &lineHeight);
// horizontal
float extraSpace = fAlignment == B_ALIGN_LEFT ?
ceilf(bounds.IntegerWidth() / 2) : 0.0;
float extraSpace = ceilf(bounds.IntegerWidth() / 2);
if (point.x < bounds.left)
xDiff = point.x - bounds.left - extraSpace;
else if (point.x > bounds.right)
xDiff = point.x - bounds.right + extraSpace;
else if (point.x > bounds.right)
xDiff = point.x - bounds.left - extraSpace;
// vertical
if (point.y < bounds.top)
@ -2098,9 +2176,7 @@ BTextView::ScrollToOffset(int32 offset)
yDiff = point.y + lineHeight - bounds.bottom;
}
// prevent negative scroll offset
if (bounds.left + xDiff < 0.0)
xDiff = -bounds.left;
// prevent negative scroll offset in y
if (bounds.top + yDiff < 0.0)
yDiff = -bounds.top;
@ -2150,11 +2226,13 @@ BTextView::SetTextRect(BRect rect)
return;
if (!fWrap) {
rect.right = Bounds().right - fLayoutData->rightInset;
rect.bottom = Bounds().bottom - fLayoutData->bottomInset;
rect.right = Bounds().right;
rect.bottom = Bounds().bottom;
}
fLayoutData->UpdateInsets(Bounds().OffsetToCopy(B_ORIGIN), rect);
_UpdateInsets(rect);
fTextRect = rect;
_ResetTextRect();
}
@ -2180,7 +2258,7 @@ BTextView::_ResetTextRect()
// and rewrap (potentially adjusting the right and the bottom of the text
// rect)
_Refresh(0, TextLength(), false);
_Refresh(0, fText->Length());
// Make sure that the dirty area outside the text is redrawn too.
BRegion invalid(oldTextRect | fTextRect);
@ -2203,6 +2281,8 @@ BTextView::SetInsets(float left, float top, float right, float bottom)
fLayoutData->rightInset = right;
fLayoutData->bottomInset = bottom;
fLayoutData->overridden = true;
InvalidateLayout();
Invalidate();
}
@ -2246,7 +2326,7 @@ BTextView::SetTabWidth(float width)
fTabWidth = width;
if (Window() != NULL)
_Refresh(0, fText->Length(), false);
_Refresh(0, fText->Length());
}
@ -2324,8 +2404,12 @@ BTextView::SetWordWrap(bool wrap)
fWrap = wrap;
if (wrap)
_ResetTextRect();
_Refresh(0, fText->Length(), false);
_ResetTextRect(); // calls _Refresh
else
_Refresh(0, fText->Length());
if (fEditable)
ScrollToOffset(fCaretOffset);
if (updateOnScreen) {
// show the caret, hilite the selection
@ -2477,7 +2561,7 @@ BTextView::MakeResizable(bool resize, BView* resizeView)
_NewOffscreen();
}
_Refresh(0, fText->Length(), false);
_Refresh(0, fText->Length());
}
@ -2614,7 +2698,7 @@ BTextView::GetHeightForWidth(float width, float* min, float* max,
// TODO: don't change the actual text rect!
fTextRect.right = fTextRect.left + width;
_Refresh(0, TextLength(), false);
_Refresh(0, fText->Length());
if (min != NULL)
*min = fTextRect.Height();
@ -2741,9 +2825,8 @@ BTextView::AllocRunArray(int32 entryCount, int32* outSize)
// Call constructors explicitly as the text_run_array
// was allocated with malloc (and has to, for backwards
// compatibility)
for (int32 i = 0; i < runArray->count; i++) {
for (int32 i = 0; i < runArray->count; i++)
new (&runArray->runs[i].font) BFont;
}
if (outSize != NULL)
*outSize = size;
@ -2910,9 +2993,9 @@ BTextView::InsertText(const char* text, int32 length, int32 offset,
}
}
if (fStylable && runs != NULL) {
if (fStylable && runs != NULL)
_SetRunArray(offset, offset + length, runs);
} else {
else {
// apply null-style to inserted text
_ApplyStyleRange(offset, offset + length);
}
@ -3130,7 +3213,7 @@ BTextView::_InitObject(BRect textRect, const BFont* initialFont,
fTrackingMouse = NULL;
fLayoutData = new LayoutData;
fLayoutData->UpdateInsets(Bounds().OffsetToCopy(B_ORIGIN), fTextRect);
_UpdateInsets(textRect);
fLastClickOffset = -1;
@ -3160,8 +3243,7 @@ BTextView::_HandleBackspace(int32 modifiers)
}
if (fUndo) {
TypingUndoBuffer* undoBuffer = dynamic_cast<TypingUndoBuffer*>(
fUndo);
TypingUndoBuffer* undoBuffer = dynamic_cast<TypingUndoBuffer*>(fUndo);
if (!undoBuffer) {
delete fUndo;
fUndo = undoBuffer = new TypingUndoBuffer(this);
@ -3180,7 +3262,7 @@ BTextView::_HandleBackspace(int32 modifiers)
DeleteText(fSelStart, fSelEnd);
fCaretOffset = fSelEnd = fSelStart;
_Refresh(fSelStart, fSelEnd, true);
_Refresh(fSelStart, fSelEnd, fCaretOffset);
}
@ -3382,8 +3464,7 @@ BTextView::_HandleDelete(int32 modifiers)
}
if (fUndo) {
TypingUndoBuffer* undoBuffer = dynamic_cast<TypingUndoBuffer*>(
fUndo);
TypingUndoBuffer* undoBuffer = dynamic_cast<TypingUndoBuffer*>(fUndo);
if (!undoBuffer) {
delete fUndo;
fUndo = undoBuffer = new TypingUndoBuffer(this);
@ -3402,7 +3483,7 @@ BTextView::_HandleDelete(int32 modifiers)
DeleteText(fSelStart, fSelEnd);
fCaretOffset = fSelEnd = fSelStart;
_Refresh(fSelStart, fSelEnd, true);
_Refresh(fSelStart, fSelEnd, fCaretOffset);
}
@ -3632,10 +3713,10 @@ BTextView::_HandleAlphaKey(const char* bytes, int32 numBytes)
\param fromOffset The offset from where to refresh.
\param toOffset The offset where to refresh to.
\param scroll If \c true, scroll the view to the end offset.
\param scrollTo Scroll the view to \a scrollTo offset if not \c INT32_MIN.
*/
void
BTextView::_Refresh(int32 fromOffset, int32 toOffset, bool scroll)
BTextView::_Refresh(int32 fromOffset, int32 toOffset, int32 scrollTo)
{
// TODO: Cleanup
float saveHeight = fTextRect.Height();
@ -3671,8 +3752,8 @@ BTextView::_Refresh(int32 fromOffset, int32 toOffset, bool scroll)
// draw only those lines that are visible
int32 fromVisible = _LineAt(BPoint(0.0f, bounds.top));
int32 toVisible = _LineAt(BPoint(0.0f, bounds.bottom));
fromLine = max_c(fromVisible, fromLine);
toLine = min_c(toLine, toVisible);
fromLine = std::max(fromVisible, fromLine);
toLine = std::min(toLine, toVisible);
_AutoResize(false);
@ -3691,8 +3772,8 @@ BTextView::_Refresh(int32 fromOffset, int32 toOffset, bool scroll)
if (newHeight != saveHeight || fMinTextRectWidth != saveWidth)
_UpdateScrollbars();
if (scroll)
ScrollToOffset(fSelEnd);
if (scrollTo != INT32_MIN)
ScrollToOffset(scrollTo);
Flush();
}
@ -3708,7 +3789,13 @@ BTextView::_RecalculateLineBreaks(int32* startLine, int32* endLine)
{
CALLED();
// are we insane?
float width = fTextRect.Width();
// don't try to compute anything if the text rect is not set
if (!fTextRect.IsValid() || width == 0)
return;
// sanity check
*startLine = (*startLine < 0) ? 0 : *startLine;
*endLine = (*endLine > fLines->NumLines() - 1) ? fLines->NumLines() - 1
: *endLine;
@ -3716,13 +3803,6 @@ BTextView::_RecalculateLineBreaks(int32* startLine, int32* endLine)
int32 textLength = fText->Length();
int32 lineIndex = (*startLine > 0) ? *startLine - 1 : 0;
int32 recalThreshold = (*fLines)[*endLine + 1]->offset;
float width = max_c(fTextRect.Width(), 10);
// TODO: The minimum width of 10 is a work around for the following
// problem: If the text rect is too small, we are not calculating any
// line heights, not even for the first line. Maybe this is a bug
// in the algorithm, but other places in the class rely on at least
// the first line to return a valid height. Maybe "10" should really
// be the width of the very first glyph instead.
STELine* curLine = (*fLines)[lineIndex];
STELine* nextLine = curLine + 1;
@ -3781,16 +3861,44 @@ BTextView::_RecalculateLineBreaks(int32* startLine, int32* endLine)
// has always a width of 0
(*fLines)[fLines->NumLines()]->width = 0;
// update the text rect
// update text rect
fTextRect.left = Bounds().left + fLayoutData->leftInset;
fTextRect.right = Bounds().right - fLayoutData->rightInset;
// always set text rect bottom
float newHeight = TextHeight(0, fLines->NumLines() - 1);
fTextRect.bottom = fTextRect.top + newHeight;
if (!fWrap) {
fMinTextRectWidth = fLines->MaxWidth();
fTextRect.right = ceilf(fTextRect.left + fMinTextRectWidth);
// expand width if needed
switch (fAlignment) {
default:
case B_ALIGN_LEFT:
// grow right
fTextRect.right = std::max(fTextRect.right,
fTextRect.left + fMinTextRectWidth);
break;
case B_ALIGN_RIGHT:
// grow left
fTextRect.left = std::min(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);
}
break;
}
}
*endLine = lineIndex - 1;
*startLine = min_c(*startLine, *endLine);
*startLine = std::min(*startLine, *endLine);
}
@ -3881,7 +3989,7 @@ BTextView::_FindLineBreak(int32 fromOffset, float* _ascent, float* _descent,
}
}
delta = max_c(delta, 1);
delta = std::max(delta, (int32)1);
// do not include B_ENTER-terminator into width & height calculations
deltaWidth = _TabExpandedStyledWidth(offset,
@ -3912,8 +4020,8 @@ BTextView::_FindLineBreak(int32 fromOffset, float* _ascent, float* _descent,
}
}
*_ascent = max_c(ascent, *_ascent);
*_descent = max_c(descent, *_descent);
*_ascent = std::max(ascent, *_ascent);
*_descent = std::max(descent, *_descent);
offset += delta;
delta = 0;
@ -3936,12 +4044,12 @@ BTextView::_FindLineBreak(int32 fromOffset, float* _ascent, float* _descent,
break;
}
*_ascent = max_c(ascent, *_ascent);
*_descent = max_c(descent, *_descent);
*_ascent = std::max(ascent, *_ascent);
*_descent = std::max(descent, *_descent);
}
}
return min_c(offset, limit);
return std::min(offset, limit);
}
@ -4138,8 +4246,8 @@ BTextView::_StyledWidth(int32 fromOffset, int32 length, float* _ascent,
int32 numBytes;
while ((numBytes = fStyles->Iterate(fromOffset, length, fInline, &font,
NULL, &ascent, &descent)) != 0) {
maxAscent = max_c(ascent, maxAscent);
maxDescent = max_c(descent, maxDescent);
maxAscent = std::max(ascent, maxAscent);
maxDescent = std::max(descent, maxDescent);
#if USE_WIDTHBUFFER
// Use _BWidthBuffer_ if possible
@ -4186,13 +4294,13 @@ BTextView::_DoInsertText(const char* text, int32 length, int32 offset,
{
_CancelInputMethod();
if (TextLength() + length > MaxBytes())
if (fText->Length() + length > MaxBytes())
return;
if (fSelStart != fSelEnd)
Select(fSelStart, fSelStart);
const int32 textLength = TextLength();
const int32 textLength = fText->Length();
if (offset > textLength)
offset = textLength;
@ -4200,7 +4308,7 @@ BTextView::_DoInsertText(const char* text, int32 length, int32 offset,
InsertText(text, length, offset, runs);
// recalc line breaks and draw the text
_Refresh(offset, offset + length, false);
_Refresh(offset, offset + length);
}
@ -4224,12 +4332,11 @@ BTextView::_DrawLine(BView* view, const int32 &lineNum,
startLeft = PointAt(line->offset).x;
} else
startLeft = PointAt(startOffset).x;
}
else if (fAlignment != B_ALIGN_LEFT) {
} else if (fAlignment != B_ALIGN_LEFT) {
float alignmentOffset = fTextRect.Width() - LineWidth(lineNum);
if (fAlignment == B_ALIGN_CENTER)
alignmentOffset /= 2;
startLeft = fTextRect.left + alignmentOffset;
alignmentOffset = floorf(alignmentOffset / 2);
startLeft += alignmentOffset;
}
int32 length = (line + 1)->offset;
@ -4242,7 +4349,8 @@ BTextView::_DrawLine(BView* view, const int32 &lineNum,
if (ByteAt((line + 1)->offset - 1) == B_ENTER)
length--;
view->MovePenTo(startLeft, line->origin + line->ascent + fTextRect.top + 1);
view->MovePenTo(startLeft,
line->origin + line->ascent + fTextRect.top + 1);
if (erase) {
eraseRect.top = line->origin + fTextRect.top;
@ -4268,7 +4376,7 @@ BTextView::_DrawLine(BView* view, const int32 &lineNum,
view->SetFont(font);
view->SetHighColor(*color);
tabChars = min_c(numBytes, length);
tabChars = std::min(numBytes, length);
do {
foundTab = fText->FindChar(B_TAB, offset, &tabChars);
if (foundTab) {
@ -4316,24 +4424,44 @@ BTextView::_DrawLine(BView* view, const int32 &lineNum,
view->PopState();
}
int32 returnedBytes = tabChars;
const char* stringToDraw = fText->GetString(offset, &returnedBytes);
view->SetDrawingMode(textRenderingMode);
view->DrawString(stringToDraw, returnedBytes);
if (foundTab) {
float penPos = PenLocation().x - fTextRect.left;
float tabWidth = _ActualTabWidth(penPos);
if (numTabs > 1)
tabWidth += ((numTabs - 1) * fTabWidth);
switch (fAlignment) {
default:
case B_ALIGN_LEFT:
// nothing more to do
break;
case B_ALIGN_RIGHT:
// subtract distance from left to line
penPos -= fTextRect.Width() - LineWidth(lineNum);
break;
case B_ALIGN_CENTER:
// subtract half distance from left to line
penPos -= floorf((fTextRect.Width()
- LineWidth(lineNum)) / 2);
break;
}
float tabWidth = _ActualTabWidth(penPos);
// add in the rest of the tabs (if there are any)
tabWidth += ((numTabs - 1) * fTabWidth);
// move pen by tab(s) width
view->MovePenBy(tabWidth, 0.0);
tabChars += numTabs;
} else {
int32 size = tabChars;
const char* stringToDraw = fText->GetString(offset, &size);
view->SetDrawingMode(textRenderingMode);
view->DrawString(stringToDraw, size);
}
offset += tabChars;
length -= tabChars;
numBytes -= tabChars;
tabChars = min_c(numBytes, length);
tabChars = std::min(numBytes, length);
numTabs = 0;
} while (foundTab && tabChars > 0);
}
@ -4349,8 +4477,8 @@ BTextView::_DrawLines(int32 startLine, int32 endLine, int32 startOffset,
// clip the text
BRect textRect(fTextRect);
float minWidth
= Bounds().Width() - fLayoutData->leftInset - fLayoutData->rightInset;
float minWidth = Bounds().Width() - fLayoutData->leftInset
- fLayoutData->rightInset;
if (textRect.Width() < minWidth)
textRect.right = textRect.left + minWidth;
BRect clipRect = Bounds() & textRect;
@ -4464,7 +4592,7 @@ BTextView::_DrawCaret(int32 offset, bool visible)
{
float lineHeight;
BPoint caretPoint = PointAt(offset, &lineHeight);
caretPoint.x = min_c(caretPoint.x, fTextRect.right);
caretPoint.x = std::min(caretPoint.x, fTextRect.right);
BRect caretRect;
caretRect.left = caretRect.right = caretPoint.x;
@ -4738,8 +4866,8 @@ BTextView::_MessageDropped(BMessage* message, BPoint where, BPoint offset)
return false;
int32 dropOffset = OffsetAt(where);
if (dropOffset > TextLength())
dropOffset = TextLength();
if (dropOffset > fText->Length())
dropOffset = fText->Length();
// if this view initiated the drag, move instead of copy
if (internalDrop) {
@ -4795,11 +4923,10 @@ BTextView::_PerformAutoScrolling()
// R5 does a pretty soft auto-scroll, we try to do the same by
// simply scrolling the distance between cursor and border
if (fWhere.x > bounds.right) {
if (fWhere.x > bounds.right)
scrollBy.x = fWhere.x - bounds.right;
} else if (fWhere.x < bounds.left) {
else if (fWhere.x < bounds.left)
scrollBy.x = fWhere.x - bounds.left; // negative value
}
// prevent from scrolling out of view
if (scrollBy.x != 0.0) {
@ -4812,11 +4939,10 @@ BTextView::_PerformAutoScrolling()
if (CountLines() > 1) {
// scroll in Y only if multiple lines!
if (fWhere.y > bounds.bottom) {
if (fWhere.y > bounds.bottom)
scrollBy.y = fWhere.y - bounds.bottom;
} else if (fWhere.y < bounds.top) {
else if (fWhere.y < bounds.top)
scrollBy.y = fWhere.y - bounds.top; // negative value
}
// prevent from scrolling out of view
if (scrollBy.y != 0.0) {
@ -4849,25 +4975,29 @@ BTextView::_UpdateScrollbars()
+ fLayoutData->leftInset + fLayoutData->rightInset);
long maxRange = dataWidth - viewWidth;
maxRange = max_c(maxRange, 0);
maxRange = std::max(maxRange, 0l);
horizontalScrollBar->SetRange(0, (float)maxRange);
horizontalScrollBar->SetProportion((float)viewWidth / (float)dataWidth);
horizontalScrollBar->SetSteps(kHorizontalScrollBarStep, dataWidth / 10);
horizontalScrollBar->SetProportion((float)viewWidth
/ (float)dataWidth);
horizontalScrollBar->SetSteps(kHorizontalScrollBarStep,
dataWidth / 10);
}
// how about a vertical scroll bar?
if (verticalScrollBar != NULL) {
long viewHeight = bounds.IntegerHeight();
long dataHeight = (long)ceilf(fTextRect.IntegerHeight()
+ fLayoutData->topInset + fLayoutData->bottomInset);
long dataHeight = (long)ceilf(fLayoutData->topInset
+ fTextRect.IntegerHeight() + fLayoutData->bottomInset);
long maxRange = dataHeight - viewHeight;
maxRange = max_c(maxRange, 0);
maxRange = std::max(maxRange, 0l);
verticalScrollBar->SetRange(0, maxRange);
verticalScrollBar->SetProportion((float)viewHeight / (float)dataHeight);
verticalScrollBar->SetSteps(kVerticalScrollBarStep, viewHeight);
verticalScrollBar->SetProportion((float)viewHeight
/ (float)dataHeight);
verticalScrollBar->SetSteps(kVerticalScrollBarStep,
viewHeight);
}
}
@ -4911,25 +5041,25 @@ BTextView::_AutoResize(bool redraw)
return;
BRect bounds = Bounds();
float oldWidth = bounds.Width();
float newWidth = ceilf(fLayoutData->leftInset + fTextRect.Width()
+ fLayoutData->rightInset);
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;
fContainerView->MoveBy(ceilf(oldWidth - newWidth) / 2, 0);
} else if (fAlignment == B_ALIGN_RIGHT) {
} else if (fAlignment == B_ALIGN_RIGHT)
fContainerView->MoveBy(ceilf(oldWidth - newWidth), 0);
}
// resize container view
fContainerView->ResizeBy(ceilf(newWidth - oldWidth), 0);
}
if (redraw)
_RequestDrawLines(0, 0);
@ -5471,7 +5601,7 @@ BTextView::_CountProperties(BMessage* specifier, int32 form,
CALLED();
if (strcmp(property, "Text") == 0) {
reply->what = B_REPLY;
reply->AddInt32("result", TextLength());
reply->AddInt32("result", fText->Length());
reply->AddInt32("error", B_OK);
return true;
}
@ -5539,7 +5669,7 @@ BTextView::_HandleInputMethodChanged(BMessage* message)
}
if (confirmed) {
_Refresh(fSelStart, fSelEnd, true);
_Refresh(fSelStart, fSelEnd, fSelEnd);
_ShowCaret();
// now we need to feed ourselves the individual characters as if the
@ -5565,7 +5695,7 @@ BTextView::_HandleInputMethodChanged(BMessage* message)
prevPos = currPos;
}
_Refresh(fSelStart, fSelEnd, true);
_Refresh(fSelStart, fSelEnd, fSelEnd);
} else {
// temporarily show transient state of inline input
int32 selectionStart = 0;
@ -5579,10 +5709,9 @@ BTextView::_HandleInputMethodChanged(BMessage* message)
const int32 inlineOffset = fInline->Offset();
InsertText(string, stringLen, fSelStart, NULL);
_Refresh(inlineOffset, fSelEnd, true);
_Refresh(inlineOffset, fSelEnd, fSelEnd);
_ShowCaret();
}
}
@ -5627,8 +5756,8 @@ BTextView::_CancelInputMethod()
fInline = NULL;
if (inlineInput->IsActive() && Window()) {
_Refresh(inlineInput->Offset(), fText->Length() - inlineInput->Offset(),
false);
_Refresh(inlineInput->Offset(), fText->Length()
- inlineInput->Offset());
BMessage message(B_INPUT_METHOD_EVENT);
message.AddInt32("be:opcode", B_INPUT_METHOD_STOPPED);
@ -5677,7 +5806,7 @@ BTextView::_LineAt(const BPoint& point) const
bool
BTextView::_IsOnEmptyLastLine(int32 offset) const
{
return (offset == TextLength() && offset > 0
return (offset == fText->Length() && offset > 0
&& fText->RealCharAt(offset - 1) == B_ENTER);
}
@ -5734,7 +5863,7 @@ BTextView::_ShowContextMenu(BPoint where)
GetSelection(&start, &finish);
bool canEdit = IsEditable();
int32 length = TextLength();
int32 length = fText->Length();
BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
@ -5804,6 +5933,39 @@ BTextView::_FilterDisallowedChars(char* text, ssize_t& length,
}
void
BTextView::_UpdateInsets(const BRect& rect)
{
// do not update insets if SetInsets() was called
if (fLayoutData->overridden)
return;
const BRect& bounds = Bounds();
// we disallow negative insets, as they would cause parts of the
// text to be hidden
fLayoutData->leftInset = rect.left >= bounds.left
? rect.left - bounds.left : 0;
fLayoutData->topInset = rect.top >= bounds.top
? rect.top - bounds.top : 0;
fLayoutData->rightInset = bounds.right >= rect.right
? bounds.right - rect.right : 0;
fLayoutData->bottomInset = bounds.bottom >= rect.bottom
? bounds.bottom - rect.bottom : 0;
// only add default insets if text rect is set to bounds
if (rect == bounds && (fEditable || fSelectable)) {
float hPadding = be_control_look->DefaultLabelSpacing();
float hInset = floorf(hPadding / 2.0f);
float vInset = 1;
fLayoutData->leftInset += hInset;
fLayoutData->topInset += vInset;
fLayoutData->rightInset += hInset;
fLayoutData->bottomInset += vInset;
}
}
// #pragma mark - BTextView::TextTrackState

View File

@ -366,11 +366,14 @@ BTextWidget::StartEdit(BRect bounds, BPoseView* view, BPose* pose)
return;
}
// TODO fix text rect being off by a pixel on some files
// get bounds with full text length
BRect rect(bounds);
BRect textRect(bounds);
rect.OffsetBy(-2, -1);
rect.right += 1;
// label offset
rect.OffsetBy(1, -2);
BFont font;
view->GetFont(&font);
@ -378,22 +381,21 @@ BTextWidget::StartEdit(BRect bounds, BPoseView* view, BPose* pose)
&font, 0, B_FOLLOW_ALL, B_WILL_DRAW);
textView->SetWordWrap(false);
textView->SetInsets(2, 2, 2, 2);
DisallowMetaKeys(textView);
fText->SetUpEditing(textView);
textView->AddFilter(new BMessageFilter(B_KEY_DOWN, TextViewFilter));
rect.right = rect.left + textView->LineWidth() + 3;
// center new width, if necessary
if (view->ViewMode() == kIconMode
|| (view->ViewMode() == kListMode && fAlignment == B_ALIGN_CENTER)) {
rect.OffsetBy(bounds.Width() / 2 - rect.Width() / 2, 0);
}
rect.right = rect.left + textView->LineWidth();
rect.bottom = rect.top + textView->LineHeight() - 1;
// enlarge rect by inset amount
rect.InsetBy(-2, -2);
// undo label offset
textRect = rect.OffsetToCopy(-1, 2);
rect.bottom = rect.top + textView->LineHeight() + 1;
textRect = rect.OffsetToCopy(2, 1);
textRect.right -= 3;
textRect.bottom--;
textView->SetTextRect(textRect);
BPoint origin = view->LeftTop();

View File

@ -302,7 +302,6 @@ HeaderView::Draw(BRect)
// Font information
font_height fontMetrics;
BFont currentFont;
float lineHeight = 0;
float lineBase = 0;
// Draw the main title if the user is not currently editing it
@ -311,7 +310,6 @@ HeaderView::Draw(BRect)
SetFontSize(be_bold_font->Size());
GetFont(&currentFont);
currentFont.GetHeight(&fontMetrics);
lineHeight = CurrentFontHeight() + 5;
lineBase = fTitleRect.bottom - fontMetrics.descent;
SetHighColor(labelColor);
MovePenTo(BPoint(fIconRect.right + 6, lineBase));

View File

@ -146,7 +146,8 @@ PreviewView::AddPreview()
fNoPreview->SetExplicitSize(BSize(previewWidth, previewHeight));
fNoPreview->ResizeTo(previewWidth, previewHeight);
fNoPreview->SetInsets(0, previewHeight / 3, 0 , 0);
fNoPreview->SetTextRect(BRect(0, 0, previewWidth, previewHeight));
fNoPreview->SetInsets(0, previewHeight / 3, 0, 0);
return fSaverView;
}