* First steps towards making BTextView behave well within the layout management.
* The class calculates a minimum width now, which is based on the line height, this may also fix the bug with the small text inputs in the Pe Find window. * Added TODOs about implementing GetHeightForWidth(), which may be a good idea when a BTextView is used as non-editable informative text in an interface, and one wants to make sure that the entire text is shown. * Replaced the call to _Refresh() in Draw(), which recalculates all the line breaks for no reason with _DrawLines() again. The TODO mentioned that text will be drawn without drawing the background first, but maybe this is a relict from times where Draw() was invoked directly? At least I cannot see any negative consequences, and this should be much more efficient. (Other than that, this patch should hopefully have no potential negative side effects...<crosses fingers>) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27670 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
53aaed9f6b
commit
8864334702
|
@ -210,6 +210,24 @@ public:
|
|||
|
||||
virtual void ResizeToPreferred();
|
||||
virtual void GetPreferredSize(float* _width, float* _height);
|
||||
|
||||
virtual BSize MinSize();
|
||||
virtual BSize MaxSize();
|
||||
virtual BSize PreferredSize();
|
||||
|
||||
virtual bool HasHeightForWidth();
|
||||
virtual void GetHeightForWidth(float width, float* min,
|
||||
float* max, float* preferred);
|
||||
|
||||
virtual void InvalidateLayout(bool descendants = false);
|
||||
|
||||
protected:
|
||||
virtual void DoLayout();
|
||||
|
||||
private:
|
||||
void _ValidateLayoutData();
|
||||
|
||||
public:
|
||||
virtual void AllAttached();
|
||||
virtual void AllDetached();
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <Clipboard.h>
|
||||
#include <Debug.h>
|
||||
#include <Input.h>
|
||||
#include <LayoutUtils.h>
|
||||
#include <MessageRunner.h>
|
||||
#include <PropertyInfo.h>
|
||||
#include <Region.h>
|
||||
|
@ -48,13 +49,23 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
//#define TRACE_TEXTVIEW
|
||||
#ifdef TRACE_TEXTVIEW
|
||||
# define CALLED() printf("%s\n", __PRETTY_FUNCTION__)
|
||||
#undef TRACE
|
||||
#undef CALLED
|
||||
//#define TRACE_TEXT_VIEW
|
||||
#ifdef TRACE_TEXT_VIEW
|
||||
# include <FunctionTracer.h>
|
||||
static int32 sFunctionDepth = -1;
|
||||
# define CALLED(x...) FunctionTracer _ft("BTextView", __FUNCTION__, \
|
||||
sFunctionDepth)
|
||||
# define TRACE(x...) { BString _to; \
|
||||
_to.Append(' ', (sFunctionDepth + 1) * 2); \
|
||||
printf("%s", _to.String()); printf(x); }
|
||||
#else
|
||||
# define CALLED()
|
||||
# define CALLED(x...)
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
#define USE_WIDTHBUFFER 1
|
||||
#define USE_DOUBLEBUFFERING 0
|
||||
|
||||
|
@ -111,22 +122,30 @@ private:
|
|||
|
||||
|
||||
struct BTextView::LayoutData {
|
||||
LayoutData(float leftInset, float topInset, float rightInset,
|
||||
float bottomInset)
|
||||
: leftInset(leftInset),
|
||||
topInset(topInset),
|
||||
rightInset(rightInset),
|
||||
bottomInset(bottomInset),
|
||||
LayoutData()
|
||||
: leftInset(0),
|
||||
topInset(0),
|
||||
rightInset(0),
|
||||
bottomInset(0),
|
||||
valid(false)
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateInsets(const BRect& bounds, const BRect& textRect)
|
||||
{
|
||||
leftInset = textRect.left - bounds.left;
|
||||
topInset = textRect.top - bounds.top;
|
||||
rightInset = bounds.right - textRect.right;
|
||||
bottomInset = bounds.bottom - textRect.bottom;
|
||||
}
|
||||
|
||||
float leftInset;
|
||||
float topInset;
|
||||
float rightInset;
|
||||
float bottomInset;
|
||||
|
||||
BSize min;
|
||||
BSize preferred;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
|
@ -242,7 +261,7 @@ BTextView::BTextView(const char* name, uint32 flags)
|
|||
: BView(name, flags | B_FRAME_EVENTS | B_PULSE_NEEDED
|
||||
| B_INPUT_METHOD_AWARE)
|
||||
{
|
||||
// TODO: ...
|
||||
_InitObject(Bounds(), NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -259,7 +278,7 @@ BTextView::BTextView(const char* name, const BFont* initialFont,
|
|||
: BView(name, flags | B_FRAME_EVENTS | B_PULSE_NEEDED
|
||||
| B_INPUT_METHOD_AWARE)
|
||||
{
|
||||
// TODO: ...
|
||||
_InitObject(Bounds(), initialFont, initialColor);
|
||||
}
|
||||
|
||||
|
||||
|
@ -362,6 +381,7 @@ BTextView::~BTextView()
|
|||
delete fUndo;
|
||||
delete fClickRunner;
|
||||
delete fDragRunner;
|
||||
delete fLayoutData;
|
||||
}
|
||||
|
||||
|
||||
|
@ -487,14 +507,10 @@ void
|
|||
BTextView::Draw(BRect updateRect)
|
||||
{
|
||||
// what lines need to be drawn?
|
||||
int32 startLine = LineAt(BPoint(0.0f, updateRect.top));
|
||||
int32 endLine = LineAt(BPoint(0.0f, updateRect.bottom));
|
||||
int32 startLine = LineAt(BPoint(0.0, updateRect.top));
|
||||
int32 endLine = LineAt(BPoint(0.0, updateRect.bottom));
|
||||
|
||||
// TODO: _DrawLines draw the text over and over, causing the text to
|
||||
// antialias against itself. In theory we should use an offscreen bitmap
|
||||
// to draw the text which would eliminate the problem.
|
||||
//_DrawLines(startLine, endLine);
|
||||
_Refresh(OffsetAt(startLine), OffsetAt(endLine + 1), false, false);
|
||||
_DrawLines(startLine, endLine);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1555,7 +1571,10 @@ BTextView::SetFontAndColor(int32 startOffset, int32 endOffset,
|
|||
fStyles->SetStyleRange(startOffset, endOffset, fText->Length(),
|
||||
fontMode, &newFont, color);
|
||||
|
||||
if (fontMode & B_FONT_FAMILY_AND_STYLE || fontMode & B_FONT_SIZE) {
|
||||
if ((fontMode & (B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE)) != 0) {
|
||||
// TODO: maybe only invalidate the layout (depending on
|
||||
// B_SUPPORTS_LAYOUT) and have it _Refresh() automatically?
|
||||
InvalidateLayout();
|
||||
// recalc the line breaks and redraw with new style
|
||||
_Refresh(startOffset, endOffset, startOffset != endOffset, false);
|
||||
} else {
|
||||
|
@ -2052,6 +2071,9 @@ BTextView::Highlight(int32 startOffset, int32 endOffset)
|
|||
}
|
||||
|
||||
|
||||
// #pragma mark - configuration
|
||||
|
||||
|
||||
/*! \brief Sets the BTextView's text rectangle to be the same as the passed
|
||||
rect.
|
||||
\param rect A BRect.
|
||||
|
@ -2064,6 +2086,10 @@ BTextView::SetTextRect(BRect rect)
|
|||
|
||||
fTextRect = rect;
|
||||
fMinTextRectWidth = fTextRect.Width();
|
||||
// used in auto-resizing mode to keep the text rect from
|
||||
// shrinking below a certain value.
|
||||
|
||||
fLayoutData->UpdateInsets(Bounds(), fTextRect);
|
||||
|
||||
if (Window() != NULL)
|
||||
Invalidate();
|
||||
|
@ -2085,10 +2111,19 @@ BTextView::TextRect() const
|
|||
void
|
||||
BTextView::SetInsets(float left, float top, float right, float bottom)
|
||||
{
|
||||
// TODO:
|
||||
// * store in layout data
|
||||
// * invalidate layout
|
||||
// * invalidate view
|
||||
if (fLayoutData->leftInset == left
|
||||
&& fLayoutData->topInset == top
|
||||
&& fLayoutData->rightInset == right
|
||||
&& fLayoutData->bottomInset == bottom)
|
||||
return;
|
||||
|
||||
fLayoutData->leftInset = left;
|
||||
fLayoutData->topInset = top;
|
||||
fLayoutData->rightInset = right;
|
||||
fLayoutData->bottomInset = bottom;
|
||||
|
||||
InvalidateLayout();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2497,22 +2532,170 @@ BTextView::IsTypingHidden() const
|
|||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
BTextView::ResizeToPreferred()
|
||||
{
|
||||
float widht, height;
|
||||
GetPreferredSize(&widht, &height);
|
||||
BView::ResizeTo(widht, height);
|
||||
BView::ResizeToPreferred();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTextView::GetPreferredSize(float *width, float *height)
|
||||
BTextView::GetPreferredSize(float* _width, float* _height)
|
||||
{
|
||||
BView::GetPreferredSize(width, height);
|
||||
CALLED();
|
||||
|
||||
_ValidateLayoutData();
|
||||
|
||||
if (_width)
|
||||
*_width = fLayoutData->min.width;
|
||||
|
||||
if (_height)
|
||||
*_height = fLayoutData->min.height;
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
BTextView::MinSize()
|
||||
{
|
||||
CALLED();
|
||||
|
||||
_ValidateLayoutData();
|
||||
return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min);
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
BTextView::MaxSize()
|
||||
{
|
||||
CALLED();
|
||||
|
||||
return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
|
||||
BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
BTextView::PreferredSize()
|
||||
{
|
||||
CALLED();
|
||||
|
||||
_ValidateLayoutData();
|
||||
return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
|
||||
fLayoutData->preferred);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BTextView::HasHeightForWidth()
|
||||
{
|
||||
// TODO: When not editable and not embedded in a scroll view, we should
|
||||
// assume that all text is supposed to be visible.
|
||||
return BView::HasHeightForWidth();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTextView::GetHeightForWidth(float width, float* min, float* max,
|
||||
float* preferred)
|
||||
{
|
||||
// TODO: See above and implement.
|
||||
BView::GetHeightForWidth(width, min, max, preferred);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTextView::InvalidateLayout(bool descendants)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
fLayoutData->valid = false;
|
||||
|
||||
BView::InvalidateLayout(descendants);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTextView::DoLayout()
|
||||
{
|
||||
// Bail out, if we shan't do layout.
|
||||
if (!(Flags() & B_SUPPORTS_LAYOUT))
|
||||
return;
|
||||
|
||||
CALLED();
|
||||
|
||||
// If the user set a layout, we let the base class version call its
|
||||
// hook.
|
||||
if (GetLayout()) {
|
||||
BView::DoLayout();
|
||||
return;
|
||||
}
|
||||
|
||||
_ValidateLayoutData();
|
||||
|
||||
// validate current size
|
||||
BSize size(Bounds().Size());
|
||||
if (size.width < fLayoutData->min.width)
|
||||
size.width = fLayoutData->min.width;
|
||||
if (size.height < fLayoutData->min.height)
|
||||
size.height = fLayoutData->min.height;
|
||||
|
||||
// layout text rect
|
||||
BPoint previousLocation = fTextRect.LeftTop();
|
||||
float previousTextWidth = fTextRect.Width();
|
||||
|
||||
fTextRect = Bounds();
|
||||
fTextRect.left += fLayoutData->leftInset;
|
||||
fTextRect.top += fLayoutData->topInset;
|
||||
fTextRect.right -= fLayoutData->rightInset;
|
||||
fTextRect.bottom -= fLayoutData->bottomInset;
|
||||
|
||||
// recalculate linebreaks and invalidate if necessary
|
||||
if (previousTextWidth != fTextRect.Width()
|
||||
|| previousLocation != fTextRect.LeftTop()) {
|
||||
int32 startLine = 0;
|
||||
int32 endLine = fLines->NumLines() - 1;
|
||||
_RecalculateLineBreaks(&startLine, &endLine);
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTextView::_ValidateLayoutData()
|
||||
{
|
||||
if (fLayoutData->valid)
|
||||
return;
|
||||
|
||||
CALLED();
|
||||
|
||||
float lineHeight = ceilf(LineHeight(0));
|
||||
TRACE("line height: %.2f\n", lineHeight);
|
||||
|
||||
// compute our minimal size
|
||||
BSize min(lineHeight * 3, lineHeight);
|
||||
min.width += fLayoutData->leftInset + fLayoutData->rightInset;
|
||||
min.height += fLayoutData->topInset + fLayoutData->bottomInset;
|
||||
|
||||
fLayoutData->min = min;
|
||||
|
||||
// compute our preferred size
|
||||
fLayoutData->preferred = min;
|
||||
fLayoutData->preferred.width += lineHeight * 6;
|
||||
fLayoutData->preferred.height += lineHeight * 2;
|
||||
|
||||
fLayoutData->valid = true;
|
||||
|
||||
TRACE("width: %.2f, height: %.2f\n", min.width, min.height);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
BTextView::AllAttached()
|
||||
{
|
||||
|
@ -2840,6 +3023,7 @@ BTextView::_InitObject(BRect textRect, const BFont *initialFont,
|
|||
// When used within the layout management framework, the
|
||||
// text rect is changed to maintain constant insets.
|
||||
fMinTextRectWidth = fTextRect.Width();
|
||||
// see SetTextRect()
|
||||
fSelStart = fSelEnd = 0;
|
||||
fCaretVisible = false;
|
||||
fCaretTime = 0;
|
||||
|
@ -2867,6 +3051,9 @@ BTextView::_InitObject(BRect textRect, const BFont *initialFont,
|
|||
fDragRunner = NULL;
|
||||
fClickRunner = NULL;
|
||||
fTrackingMouse = NULL;
|
||||
|
||||
fLayoutData = new LayoutData;
|
||||
fLayoutData->UpdateInsets(Bounds(), fTextRect);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3275,6 +3462,8 @@ BTextView::_Refresh(int32 fromOffset, int32 toOffset, bool erase, bool scroll)
|
|||
void
|
||||
BTextView::_RecalculateLineBreaks(int32 *startLine, int32 *endLine)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
// are we insane?
|
||||
*startLine = (*startLine < 0) ? 0 : *startLine;
|
||||
*endLine = (*endLine > fLines->NumLines() - 1) ? fLines->NumLines() - 1
|
||||
|
@ -3283,7 +3472,13 @@ 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 = fTextRect.Width();
|
||||
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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue