* 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 ResizeToPreferred();
|
||||||
virtual void GetPreferredSize(float* _width, float* _height);
|
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 AllAttached();
|
||||||
virtual void AllDetached();
|
virtual void AllDetached();
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <Clipboard.h>
|
#include <Clipboard.h>
|
||||||
#include <Debug.h>
|
#include <Debug.h>
|
||||||
#include <Input.h>
|
#include <Input.h>
|
||||||
|
#include <LayoutUtils.h>
|
||||||
#include <MessageRunner.h>
|
#include <MessageRunner.h>
|
||||||
#include <PropertyInfo.h>
|
#include <PropertyInfo.h>
|
||||||
#include <Region.h>
|
#include <Region.h>
|
||||||
|
@ -48,13 +49,23 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
//#define TRACE_TEXTVIEW
|
#undef TRACE
|
||||||
#ifdef TRACE_TEXTVIEW
|
#undef CALLED
|
||||||
# define CALLED() printf("%s\n", __PRETTY_FUNCTION__)
|
//#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
|
#else
|
||||||
# define CALLED()
|
# define CALLED(x...)
|
||||||
|
# define TRACE(x...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define USE_WIDTHBUFFER 1
|
#define USE_WIDTHBUFFER 1
|
||||||
#define USE_DOUBLEBUFFERING 0
|
#define USE_DOUBLEBUFFERING 0
|
||||||
|
|
||||||
|
@ -111,22 +122,30 @@ private:
|
||||||
|
|
||||||
|
|
||||||
struct BTextView::LayoutData {
|
struct BTextView::LayoutData {
|
||||||
LayoutData(float leftInset, float topInset, float rightInset,
|
LayoutData()
|
||||||
float bottomInset)
|
: leftInset(0),
|
||||||
: leftInset(leftInset),
|
topInset(0),
|
||||||
topInset(topInset),
|
rightInset(0),
|
||||||
rightInset(rightInset),
|
bottomInset(0),
|
||||||
bottomInset(bottomInset),
|
|
||||||
valid(false)
|
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 leftInset;
|
||||||
float topInset;
|
float topInset;
|
||||||
float rightInset;
|
float rightInset;
|
||||||
float bottomInset;
|
float bottomInset;
|
||||||
|
|
||||||
BSize min;
|
BSize min;
|
||||||
|
BSize preferred;
|
||||||
bool valid;
|
bool valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -242,7 +261,7 @@ BTextView::BTextView(const char* name, uint32 flags)
|
||||||
: BView(name, flags | B_FRAME_EVENTS | B_PULSE_NEEDED
|
: BView(name, flags | B_FRAME_EVENTS | B_PULSE_NEEDED
|
||||||
| B_INPUT_METHOD_AWARE)
|
| 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
|
: BView(name, flags | B_FRAME_EVENTS | B_PULSE_NEEDED
|
||||||
| B_INPUT_METHOD_AWARE)
|
| B_INPUT_METHOD_AWARE)
|
||||||
{
|
{
|
||||||
// TODO: ...
|
_InitObject(Bounds(), initialFont, initialColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,6 +381,7 @@ BTextView::~BTextView()
|
||||||
delete fUndo;
|
delete fUndo;
|
||||||
delete fClickRunner;
|
delete fClickRunner;
|
||||||
delete fDragRunner;
|
delete fDragRunner;
|
||||||
|
delete fLayoutData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -487,14 +507,10 @@ void
|
||||||
BTextView::Draw(BRect updateRect)
|
BTextView::Draw(BRect updateRect)
|
||||||
{
|
{
|
||||||
// what lines need to be drawn?
|
// what lines need to be drawn?
|
||||||
int32 startLine = LineAt(BPoint(0.0f, updateRect.top));
|
int32 startLine = LineAt(BPoint(0.0, updateRect.top));
|
||||||
int32 endLine = LineAt(BPoint(0.0f, updateRect.bottom));
|
int32 endLine = LineAt(BPoint(0.0, updateRect.bottom));
|
||||||
|
|
||||||
// TODO: _DrawLines draw the text over and over, causing the text to
|
_DrawLines(startLine, endLine);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1555,7 +1571,10 @@ BTextView::SetFontAndColor(int32 startOffset, int32 endOffset,
|
||||||
fStyles->SetStyleRange(startOffset, endOffset, fText->Length(),
|
fStyles->SetStyleRange(startOffset, endOffset, fText->Length(),
|
||||||
fontMode, &newFont, color);
|
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
|
// recalc the line breaks and redraw with new style
|
||||||
_Refresh(startOffset, endOffset, startOffset != endOffset, false);
|
_Refresh(startOffset, endOffset, startOffset != endOffset, false);
|
||||||
} else {
|
} 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
|
/*! \brief Sets the BTextView's text rectangle to be the same as the passed
|
||||||
rect.
|
rect.
|
||||||
\param rect A BRect.
|
\param rect A BRect.
|
||||||
|
@ -2064,6 +2086,10 @@ BTextView::SetTextRect(BRect rect)
|
||||||
|
|
||||||
fTextRect = rect;
|
fTextRect = rect;
|
||||||
fMinTextRectWidth = fTextRect.Width();
|
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)
|
if (Window() != NULL)
|
||||||
Invalidate();
|
Invalidate();
|
||||||
|
@ -2085,10 +2111,19 @@ BTextView::TextRect() const
|
||||||
void
|
void
|
||||||
BTextView::SetInsets(float left, float top, float right, float bottom)
|
BTextView::SetInsets(float left, float top, float right, float bottom)
|
||||||
{
|
{
|
||||||
// TODO:
|
if (fLayoutData->leftInset == left
|
||||||
// * store in layout data
|
&& fLayoutData->topInset == top
|
||||||
// * invalidate layout
|
&& fLayoutData->rightInset == right
|
||||||
// * invalidate view
|
&& 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
|
void
|
||||||
BTextView::ResizeToPreferred()
|
BTextView::ResizeToPreferred()
|
||||||
{
|
{
|
||||||
float widht, height;
|
BView::ResizeToPreferred();
|
||||||
GetPreferredSize(&widht, &height);
|
|
||||||
BView::ResizeTo(widht, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
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
|
void
|
||||||
BTextView::AllAttached()
|
BTextView::AllAttached()
|
||||||
{
|
{
|
||||||
|
@ -2840,6 +3023,7 @@ BTextView::_InitObject(BRect textRect, const BFont *initialFont,
|
||||||
// When used within the layout management framework, the
|
// When used within the layout management framework, the
|
||||||
// text rect is changed to maintain constant insets.
|
// text rect is changed to maintain constant insets.
|
||||||
fMinTextRectWidth = fTextRect.Width();
|
fMinTextRectWidth = fTextRect.Width();
|
||||||
|
// see SetTextRect()
|
||||||
fSelStart = fSelEnd = 0;
|
fSelStart = fSelEnd = 0;
|
||||||
fCaretVisible = false;
|
fCaretVisible = false;
|
||||||
fCaretTime = 0;
|
fCaretTime = 0;
|
||||||
|
@ -2867,6 +3051,9 @@ BTextView::_InitObject(BRect textRect, const BFont *initialFont,
|
||||||
fDragRunner = NULL;
|
fDragRunner = NULL;
|
||||||
fClickRunner = NULL;
|
fClickRunner = NULL;
|
||||||
fTrackingMouse = 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
|
void
|
||||||
BTextView::_RecalculateLineBreaks(int32 *startLine, int32 *endLine)
|
BTextView::_RecalculateLineBreaks(int32 *startLine, int32 *endLine)
|
||||||
{
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
// are we insane?
|
// are we insane?
|
||||||
*startLine = (*startLine < 0) ? 0 : *startLine;
|
*startLine = (*startLine < 0) ? 0 : *startLine;
|
||||||
*endLine = (*endLine > fLines->NumLines() - 1) ? fLines->NumLines() - 1
|
*endLine = (*endLine > fLines->NumLines() - 1) ? fLines->NumLines() - 1
|
||||||
|
@ -3283,7 +3472,13 @@ BTextView::_RecalculateLineBreaks(int32 *startLine, int32 *endLine)
|
||||||
int32 textLength = fText->Length();
|
int32 textLength = fText->Length();
|
||||||
int32 lineIndex = (*startLine > 0) ? *startLine - 1 : 0;
|
int32 lineIndex = (*startLine > 0) ? *startLine - 1 : 0;
|
||||||
int32 recalThreshold = (*fLines)[*endLine + 1]->offset;
|
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* curLine = (*fLines)[lineIndex];
|
||||||
STELine* nextLine = curLine + 1;
|
STELine* nextLine = curLine + 1;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue