Files
haiku-beta6/src/kits/interface/TextControl.cpp
T
Stephan Aßmus 2f86ba4557 Implemented a new look for the Haiku interface controls. It was
overheard that they looked too ninety-ish.
TODO: The code behind this is work in progress. The basic idea
is to extract all drawing code into a new class BControlLook,
of which there is a global instance be_control_look, instantiated
in InterfaceDefs.cpp. At the moment, all the old drawing code is
still there, and the usage of be_control_look is inside if-bodies
checking the instance against NULL. In another words, by not
instanitating be_control_look, you can revert back to the old look.
BControlLook's job is to provide reusable methods for drawing
certain types of frames, backgrounds and labels, so that application
developers can make controls that re-use the same drawing code
as built-in controls and adopt to changes made there. I have added
the notion of "borders". Each of the frame drawing methods can be
made to draw certain borders only, which is supposed to help when
controls shall visually attach. This feature is not fully explored
at all ATM.
TODO: Update BColumnListView header view and BStringItem text
spacing. Update other apps where it makes sense to use BControlLook.
For the moment, only Tracker and LaunchBox are updated. More...
NOTE: The new look is not very radically different, so that existing
apps do not immediately look too ugly or out of place.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29221 a95241bf-73f2-0310-859d-f6bbb57e9c96
2009-02-15 18:23:19 +00:00

1256 lines
25 KiB
C++

/*
* Copyright 2001-2008, Haiku Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Frans van Nispen ([email protected])
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
/*! BTextControl displays text that can act like a control. */
#include <TextControl.h>
#include <AbstractLayoutItem.h>
#include <ControlLook.h>
#include <LayoutUtils.h>
#include <Message.h>
#include <Region.h>
#include <Window.h>
#include <binary_compatibility/Interface.h>
#include "TextInput.h"
//#define TRACE_TEXT_CONTROL
#ifdef TRACE_TEXT_CONTROL
# include <stdio.h>
# include <FunctionTracer.h>
static int32 sFunctionDepth = -1;
# define CALLED(x...) FunctionTracer _ft("BTextControl", __FUNCTION__, \
sFunctionDepth)
# define TRACE(x...) { BString _to; \
_to.Append(' ', (sFunctionDepth + 1) * 2); \
printf("%s", _to.String()); printf(x); }
#else
# define CALLED(x...)
# define TRACE(x...)
#endif
class BTextControl::LabelLayoutItem : public BAbstractLayoutItem {
public:
LabelLayoutItem(BTextControl* parent);
virtual bool IsVisible();
virtual void SetVisible(bool visible);
virtual BRect Frame();
virtual void SetFrame(BRect frame);
virtual BView* View();
virtual BSize BaseMinSize();
virtual BSize BaseMaxSize();
virtual BSize BasePreferredSize();
virtual BAlignment BaseAlignment();
private:
BTextControl* fParent;
BRect fFrame;
};
class BTextControl::TextViewLayoutItem : public BAbstractLayoutItem {
public:
TextViewLayoutItem(BTextControl* parent);
virtual bool IsVisible();
virtual void SetVisible(bool visible);
virtual BRect Frame();
virtual void SetFrame(BRect frame);
virtual BView* View();
virtual BSize BaseMinSize();
virtual BSize BaseMaxSize();
virtual BSize BasePreferredSize();
virtual BAlignment BaseAlignment();
private:
BTextControl* fParent;
BRect fFrame;
};
struct BTextControl::LayoutData {
LayoutData(float width, float height)
: label_layout_item(NULL),
text_view_layout_item(NULL),
previous_width(width),
previous_height(height),
valid(false)
{
}
LabelLayoutItem* label_layout_item;
TextViewLayoutItem* text_view_layout_item;
float previous_width; // used in FrameResized() for
float previous_height; // invalidation
font_height font_info;
float label_width;
float label_height;
BSize min;
BSize text_view_min;
bool valid;
};
// #pragma mark -
static const int32 kFrameMargin = 2;
static const int32 kLabelInputSpacing = 3;
BTextControl::BTextControl(BRect frame, const char* name, const char* label,
const char* text, BMessage* message, uint32 mask, uint32 flags)
: BControl(frame, name, label, message, mask, flags | B_FRAME_EVENTS)
{
_InitData(label, text);
_ValidateLayout();
}
BTextControl::BTextControl(const char* name, const char* label,
const char* text, BMessage* message, uint32 flags)
: BControl(name, label, message, flags | B_FRAME_EVENTS)
{
_InitData(label, text);
_ValidateLayout();
}
BTextControl::BTextControl(const char* label, const char* text,
BMessage* message)
: BControl(NULL, label, message,
B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS)
{
_InitData(label, text);
_ValidateLayout();
}
BTextControl::~BTextControl()
{
SetModificationMessage(NULL);
delete fLayoutData;
}
BTextControl::BTextControl(BMessage* archive)
: BControl(archive)
{
_InitData(Label(), NULL, archive);
int32 labelAlignment = B_ALIGN_LEFT;
int32 textAlignment = B_ALIGN_LEFT;
if (archive->HasInt32("_a_label"))
archive->FindInt32("_a_label", &labelAlignment);
if (archive->HasInt32("_a_text"))
archive->FindInt32("_a_text", &textAlignment);
SetAlignment((alignment)labelAlignment, (alignment)textAlignment);
if (archive->HasFloat("_divide"))
archive->FindFloat("_divide", &fDivider);
if (archive->HasMessage("_mod_msg")) {
BMessage* message = new BMessage;
archive->FindMessage("_mod_msg", message);
SetModificationMessage(message);
}
}
BArchivable*
BTextControl::Instantiate(BMessage* archive)
{
if (validate_instantiation(archive, "BTextControl"))
return new BTextControl(archive);
return NULL;
}
status_t
BTextControl::Archive(BMessage *data, bool deep) const
{
status_t ret = BControl::Archive(data, deep);
alignment labelAlignment, textAlignment;
GetAlignment(&labelAlignment, &textAlignment);
if (ret == B_OK)
ret = data->AddInt32("_a_label", labelAlignment);
if (ret == B_OK)
ret = data->AddInt32("_a_text", textAlignment);
if (ret == B_OK)
ret = data->AddFloat("_divide", Divider());
if (ModificationMessage() && (ret == B_OK))
ret = data->AddMessage("_mod_msg", ModificationMessage());
return ret;
}
void
BTextControl::SetText(const char *text)
{
if (InvokeKind() != B_CONTROL_INVOKED)
return;
CALLED();
fText->SetText(text);
if (IsFocus())
fText->SetInitialText();
fText->Invalidate();
}
const char *
BTextControl::Text() const
{
return fText->Text();
}
void
BTextControl::SetValue(int32 value)
{
BControl::SetValue(value);
}
status_t
BTextControl::Invoke(BMessage *message)
{
return BControl::Invoke(message);
}
BTextView *
BTextControl::TextView() const
{
return fText;
}
void
BTextControl::SetModificationMessage(BMessage *message)
{
delete fModificationMessage;
fModificationMessage = message;
}
BMessage *
BTextControl::ModificationMessage() const
{
return fModificationMessage;
}
void
BTextControl::SetAlignment(alignment labelAlignment, alignment textAlignment)
{
fText->SetAlignment(textAlignment);
fText->AlignTextRect();
if (fLabelAlign != labelAlignment) {
fLabelAlign = labelAlignment;
Invalidate();
}
}
void
BTextControl::GetAlignment(alignment* _label, alignment* _text) const
{
if (_label)
*_label = fLabelAlign;
if (_text)
*_text = fText->Alignment();
}
void
BTextControl::SetDivider(float dividingLine)
{
fDivider = floorf(dividingLine + 0.5);
_LayoutTextView();
if (Window()) {
fText->Invalidate();
Invalidate();
}
}
float
BTextControl::Divider() const
{
return fDivider;
}
void
BTextControl::Draw(BRect updateRect)
{
bool enabled = IsEnabled();
bool active = fText->IsFocus() && Window()->IsActive();
BRect rect = fText->Frame();
rect.InsetBy(-2, -2);
if (be_control_look != NULL) {
rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
uint32 flags = 0;
if (!enabled)
flags |= BControlLook::B_DISABLED;
if (active)
flags |= BControlLook::B_FOCUSED;
be_control_look->DrawTextControlBorder(this, rect, updateRect, base,
flags);
rect = Bounds();
rect.right = fDivider - kLabelInputSpacing;
// rect.right = fText->Frame().left - 2;
// rect.right -= 3;//be_control_look->DefaultLabelSpacing();
be_control_look->DrawLabel(this, Label(), rect, updateRect,
base, flags, BAlignment(fLabelAlign, B_ALIGN_MIDDLE));
return;
}
// outer bevel
rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT);
rgb_color lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT);
rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT);
rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT);
rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT);
rgb_color navigationColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR);
if (enabled)
SetHighColor(darken1);
else
SetHighColor(noTint);
StrokeLine(rect.LeftBottom(), rect.LeftTop());
StrokeLine(rect.RightTop());
if (enabled)
SetHighColor(lighten2);
else
SetHighColor(lighten1);
StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
StrokeLine(BPoint(rect.right, rect.top + 1.0f), rect.RightBottom());
// inner bevel
rect.InsetBy(1.0f, 1.0f);
if (active) {
SetHighColor(navigationColor);
StrokeRect(rect);
} else {
if (enabled)
SetHighColor(darken4);
else
SetHighColor(darken2);
StrokeLine(rect.LeftTop(), rect.LeftBottom());
StrokeLine(rect.LeftTop(), rect.RightTop());
SetHighColor(noTint);
StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
StrokeLine(BPoint(rect.right, rect.top + 1.0f));
}
// label
if (Label()) {
_ValidateLayoutData();
font_height& fontHeight = fLayoutData->font_info;
float y = Bounds().top + (Bounds().Height() + 1 - fontHeight.ascent
- fontHeight.descent) / 2 + fontHeight.ascent;
float x;
float labelWidth = StringWidth(Label());
switch (fLabelAlign) {
case B_ALIGN_RIGHT:
x = fDivider - labelWidth - kLabelInputSpacing;
break;
case B_ALIGN_CENTER:
x = fDivider - labelWidth / 2.0;
break;
default:
x = 0.0;
break;
}
BRect labelArea(x, Bounds().top, x + labelWidth, Bounds().bottom);
if (x < fDivider && updateRect.Intersects(labelArea)) {
labelArea.right = fText->Frame().left - kLabelInputSpacing;
BRegion clipRegion(labelArea);
ConstrainClippingRegion(&clipRegion);
SetHighColor(IsEnabled() ? ui_color(B_CONTROL_TEXT_COLOR)
: tint_color(noTint, B_DISABLED_LABEL_TINT));
DrawString(Label(), BPoint(x, y));
}
}
}
void
BTextControl::MouseDown(BPoint where)
{
if (!fText->IsFocus()) {
fText->MakeFocus(true);
}
}
void
BTextControl::AttachedToWindow()
{
BControl::AttachedToWindow();
_UpdateTextViewColors(IsEnabled());
fText->MakeEditable(IsEnabled());
}
void
BTextControl::MakeFocus(bool state)
{
if (state != fText->IsFocus()) {
fText->MakeFocus(state);
if (state)
fText->SelectAll();
}
}
void
BTextControl::SetEnabled(bool enabled)
{
if (IsEnabled() == enabled)
return;
if (Window()) {
fText->MakeEditable(enabled);
if (enabled)
fText->SetFlags(fText->Flags() | B_NAVIGABLE);
else
fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
_UpdateTextViewColors(enabled);
fText->Invalidate();
Window()->UpdateIfNeeded();
}
BControl::SetEnabled(enabled);
}
void
BTextControl::GetPreferredSize(float *_width, float *_height)
{
CALLED();
_ValidateLayoutData();
if (_width) {
float minWidth = fLayoutData->min.width;
if (Label() == NULL && !(Flags() & B_SUPPORTS_LAYOUT)) {
// Indeed, only if there is no label! BeOS backwards compatible
// behavior:
minWidth = max_c(minWidth, Bounds().Width());
}
*_width = minWidth;
}
if (_height)
*_height = fLayoutData->min.height;
}
void
BTextControl::ResizeToPreferred()
{
BView::ResizeToPreferred();
fDivider = 0.0;
const char* label = Label();
if (label)
fDivider = ceil(StringWidth(label)) + 2.0;
_LayoutTextView();
}
void
BTextControl::SetFlags(uint32 flags)
{
// If the textview is navigable, set it to not navigable if needed
// Else if it is not navigable, set it to navigable if needed
if (fText->Flags() & B_NAVIGABLE) {
if (!(flags & B_NAVIGABLE))
fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
} else {
if (flags & B_NAVIGABLE)
fText->SetFlags(fText->Flags() | B_NAVIGABLE);
}
// Don't make this one navigable
flags &= ~B_NAVIGABLE;
BView::SetFlags(flags);
}
void
BTextControl::MessageReceived(BMessage *msg)
{
switch(msg->what) {
case B_SET_PROPERTY:
case B_GET_PROPERTY:
// TODO
break;
default:
BControl::MessageReceived(msg);
break;
}
}
BHandler *
BTextControl::ResolveSpecifier(BMessage *msg, int32 index,
BMessage *specifier, int32 form,
const char *property)
{
/*
BPropertyInfo propInfo(prop_list);
BHandler *target = NULL;
if (propInfo.FindMatch(message, 0, specifier, what, property) < B_OK)
return BControl::ResolveSpecifier(message, index, specifier, what,
property);
else
return this;
*/
return BControl::ResolveSpecifier(msg, index, specifier, form, property);
}
status_t
BTextControl::GetSupportedSuites(BMessage *data)
{
return BControl::GetSupportedSuites(data);
}
void
BTextControl::MouseUp(BPoint pt)
{
BControl::MouseUp(pt);
}
void
BTextControl::MouseMoved(BPoint pt, uint32 code, const BMessage *msg)
{
BControl::MouseMoved(pt, code, msg);
}
void
BTextControl::DetachedFromWindow()
{
BControl::DetachedFromWindow();
}
void
BTextControl::AllAttached()
{
BControl::AllAttached();
}
void
BTextControl::AllDetached()
{
BControl::AllDetached();
}
void
BTextControl::FrameMoved(BPoint newPosition)
{
BControl::FrameMoved(newPosition);
}
void
BTextControl::FrameResized(float width, float height)
{
CALLED();
BControl::FrameResized(width, height);
// TODO: this causes flickering still...
// changes in width
BRect bounds = Bounds();
if (bounds.Width() > fLayoutData->previous_width) {
// invalidate the region between the old and the new right border
BRect rect = bounds;
rect.left += fLayoutData->previous_width - kFrameMargin;
rect.right--;
Invalidate(rect);
} else if (bounds.Width() < fLayoutData->previous_width) {
// invalidate the region of the new right border
BRect rect = bounds;
rect.left = rect.right - kFrameMargin;
Invalidate(rect);
}
// changes in height
if (bounds.Height() > fLayoutData->previous_height) {
// invalidate the region between the old and the new bottom border
BRect rect = bounds;
rect.top += fLayoutData->previous_height - kFrameMargin;
rect.bottom--;
Invalidate(rect);
// invalidate label area
rect = bounds;
rect.right = fDivider;
Invalidate(rect);
} else if (bounds.Height() < fLayoutData->previous_height) {
// invalidate the region of the new bottom border
BRect rect = bounds;
rect.top = rect.bottom - kFrameMargin;
Invalidate(rect);
// invalidate label area
rect = bounds;
rect.right = fDivider;
Invalidate(rect);
}
fLayoutData->previous_width = bounds.Width();
fLayoutData->previous_height = bounds.Height();
TRACE("width: %.2f, height: %.2f\n", bounds.Width(), bounds.Height());
}
void
BTextControl::WindowActivated(bool active)
{
if (fText->IsFocus()) {
// invalidate to remove/show focus indication
BRect rect = fText->Frame();
rect.InsetBy(-1, -1);
Invalidate(rect);
// help out embedded text view which doesn't
// get notified of this
fText->Invalidate();
}
}
BSize
BTextControl::MinSize()
{
CALLED();
_ValidateLayoutData();
return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min);
}
BSize
BTextControl::MaxSize()
{
CALLED();
_ValidateLayoutData();
BSize max = fLayoutData->min;
max.width = B_SIZE_UNLIMITED;
return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
}
BSize
BTextControl::PreferredSize()
{
CALLED();
_ValidateLayoutData();
return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData->min);
}
void
BTextControl::InvalidateLayout(bool descendants)
{
CALLED();
fLayoutData->valid = false;
BView::InvalidateLayout(descendants);
}
BLayoutItem*
BTextControl::CreateLabelLayoutItem()
{
if (!fLayoutData->label_layout_item)
fLayoutData->label_layout_item = new LabelLayoutItem(this);
return fLayoutData->label_layout_item;
}
BLayoutItem*
BTextControl::CreateTextViewLayoutItem()
{
if (!fLayoutData->text_view_layout_item)
fLayoutData->text_view_layout_item = new TextViewLayoutItem(this);
return fLayoutData->text_view_layout_item;
}
void
BTextControl::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;
// divider
float divider = 0;
if (fLayoutData->label_layout_item && fLayoutData->text_view_layout_item) {
// We have layout items. They define the divider location.
divider = fLayoutData->text_view_layout_item->Frame().left
- fLayoutData->label_layout_item->Frame().left;
} else {
if (fLayoutData->label_width > 0)
divider = fLayoutData->label_width + 5;
}
// text view
BRect dirty(fText->Frame());
BRect textFrame(divider + 1, kFrameMargin, size.width - 2,
size.height - kFrameMargin);
// place the text view and set the divider
BLayoutUtils::AlignInFrame(fText, textFrame);
fDivider = divider;
// invalidate dirty region
dirty = dirty | fText->Frame();
dirty.InsetBy(-kFrameMargin, -kFrameMargin);
Invalidate(dirty);
}
// #pragma mark -
status_t
BTextControl::Perform(perform_code code, void* _data)
{
switch (code) {
case PERFORM_CODE_MIN_SIZE:
((perform_data_min_size*)_data)->return_value
= BTextControl::MinSize();
return B_OK;
case PERFORM_CODE_MAX_SIZE:
((perform_data_max_size*)_data)->return_value
= BTextControl::MaxSize();
return B_OK;
case PERFORM_CODE_PREFERRED_SIZE:
((perform_data_preferred_size*)_data)->return_value
= BTextControl::PreferredSize();
return B_OK;
case PERFORM_CODE_LAYOUT_ALIGNMENT:
((perform_data_layout_alignment*)_data)->return_value
= BTextControl::LayoutAlignment();
return B_OK;
case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
((perform_data_has_height_for_width*)_data)->return_value
= BTextControl::HasHeightForWidth();
return B_OK;
case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
{
perform_data_get_height_for_width* data
= (perform_data_get_height_for_width*)_data;
BTextControl::GetHeightForWidth(data->width, &data->min, &data->max,
&data->preferred);
return B_OK;
}
case PERFORM_CODE_SET_LAYOUT:
{
perform_data_set_layout* data = (perform_data_set_layout*)_data;
BTextControl::SetLayout(data->layout);
return B_OK;
}
case PERFORM_CODE_INVALIDATE_LAYOUT:
{
perform_data_invalidate_layout* data
= (perform_data_invalidate_layout*)_data;
BTextControl::InvalidateLayout(data->descendants);
return B_OK;
}
case PERFORM_CODE_DO_LAYOUT:
{
BTextControl::DoLayout();
return B_OK;
}
}
return BControl::Perform(code, _data);
}
void BTextControl::_ReservedTextControl1() {}
void BTextControl::_ReservedTextControl2() {}
void BTextControl::_ReservedTextControl3() {}
void BTextControl::_ReservedTextControl4() {}
BTextControl &
BTextControl::operator=(const BTextControl&)
{
return *this;
}
void
BTextControl::_UpdateTextViewColors(bool enabled)
{
rgb_color textColor;
rgb_color color;
BFont font;
fText->GetFontAndColor(0, &font);
if (enabled)
textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
else {
textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_DISABLED_LABEL_TINT);
}
fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
if (enabled) {
color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
} else {
color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_LIGHTEN_2_TINT);
}
fText->SetViewColor(color);
fText->SetLowColor(color);
}
void
BTextControl::_CommitValue()
{
}
void
BTextControl::_InitData(const char* label, const char* initialText,
BMessage* archive)
{
BRect bounds(Bounds());
fText = NULL;
fModificationMessage = NULL;
fLabelAlign = B_ALIGN_LEFT;
fDivider = 0.0f;
fLayoutData = new LayoutData(bounds.Width(), bounds.Height());
int32 flags = 0;
BFont font(be_plain_font);
if (!archive || !archive->HasString("_fname"))
flags |= B_FONT_FAMILY_AND_STYLE;
if (!archive || !archive->HasFloat("_fflt"))
flags |= B_FONT_SIZE;
if (flags != 0)
SetFont(&font, flags);
if (label)
fDivider = floorf(bounds.Width() / 2.0f);
uint32 navigableFlags = Flags() & B_NAVIGABLE;
if (navigableFlags != 0)
BView::SetFlags(Flags() & ~B_NAVIGABLE);
if (archive)
fText = static_cast<BPrivate::_BTextInput_*>(FindView("_input_"));
if (fText == NULL) {
BRect frame(fDivider, bounds.top, bounds.right, bounds.bottom);
// we are stroking the frame around the text view, which
// is 2 pixels wide
frame.InsetBy(kFrameMargin, kFrameMargin);
BRect textRect(frame.OffsetToCopy(B_ORIGIN));
fText = new BPrivate::_BTextInput_(frame, textRect,
B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS | navigableFlags);
AddChild(fText);
SetText(initialText);
fText->SetAlignment(B_ALIGN_LEFT);
fText->AlignTextRect();
}
}
void
BTextControl::_ValidateLayout()
{
CALLED();
_ValidateLayoutData();
ResizeTo(Bounds().Width(), fLayoutData->min.height);
_LayoutTextView();
}
void
BTextControl::_LayoutTextView()
{
CALLED();
BRect frame = Bounds();
frame.left = fDivider;
// we are stroking the frame around the text view, which
// is 2 pixels wide
frame.InsetBy(2.0, 2.0);
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);
TRACE("fText frame: (%.2f, %.2f, %.2f, %.2f)\n",
frame.left, frame.top, frame.right, frame.bottom);
}
void
BTextControl::_UpdateFrame()
{
CALLED();
if (fLayoutData->label_layout_item && fLayoutData->text_view_layout_item) {
BRect labelFrame = fLayoutData->label_layout_item->Frame();
BRect textFrame = fLayoutData->text_view_layout_item->Frame();
// update divider
fDivider = textFrame.left - labelFrame.left;
MoveTo(labelFrame.left, labelFrame.top);
BSize oldSize = Bounds().Size();
ResizeTo(textFrame.left + textFrame.Width() - labelFrame.left,
textFrame.top + textFrame.Height() - labelFrame.top);
BSize newSize = Bounds().Size();
// If the size changes, ResizeTo() will trigger a relayout, otherwise
// we need to do that explicitly.
if (newSize != oldSize)
Relayout();
}
}
void
BTextControl::_ValidateLayoutData()
{
CALLED();
if (fLayoutData->valid)
return;
// cache font height
font_height& fh = fLayoutData->font_info;
GetFontHeight(&fh);
if (Label() != NULL) {
fLayoutData->label_width = ceilf(StringWidth(Label()));
fLayoutData->label_height = ceilf(fh.ascent) + ceilf(fh.descent);
} else {
fLayoutData->label_width = 0;
fLayoutData->label_height = 0;
}
// compute the minimal divider
float divider = 0;
if (fLayoutData->label_width > 0)
divider = fLayoutData->label_width + 5;
// If we shan't do real layout, we let the current divider take influence.
if (!(Flags() & B_SUPPORTS_LAYOUT))
divider = max_c(divider, fDivider);
// get the minimal (== preferred) text view size
fLayoutData->text_view_min = fText->MinSize();
TRACE("text view min width: %.2f\n", fLayoutData->text_view_min.width);
// compute our minimal (== preferred) size
BSize min(fLayoutData->text_view_min);
min.width += 2 * kFrameMargin;
min.height += 2 * kFrameMargin;
if (divider > 0)
min.width += divider;
if (fLayoutData->label_height > min.height)
min.height = fLayoutData->label_height;
fLayoutData->min = min;
fLayoutData->valid = true;
TRACE("width: %.2f, height: %.2f\n", min.width, min.height);
}
// #pragma mark -
BTextControl::LabelLayoutItem::LabelLayoutItem(BTextControl* parent)
: fParent(parent),
fFrame()
{
}
bool
BTextControl::LabelLayoutItem::IsVisible()
{
return !fParent->IsHidden(fParent);
}
void
BTextControl::LabelLayoutItem::SetVisible(bool visible)
{
// not allowed
}
BRect
BTextControl::LabelLayoutItem::Frame()
{
return fFrame;
}
void
BTextControl::LabelLayoutItem::SetFrame(BRect frame)
{
fFrame = frame;
fParent->_UpdateFrame();
}
BView*
BTextControl::LabelLayoutItem::View()
{
return fParent;
}
BSize
BTextControl::LabelLayoutItem::BaseMinSize()
{
fParent->_ValidateLayoutData();
if (!fParent->Label())
return BSize(-1, -1);
return BSize(fParent->fLayoutData->label_width + 5,
fParent->fLayoutData->label_height);
}
BSize
BTextControl::LabelLayoutItem::BaseMaxSize()
{
return BaseMinSize();
}
BSize
BTextControl::LabelLayoutItem::BasePreferredSize()
{
return BaseMinSize();
}
BAlignment
BTextControl::LabelLayoutItem::BaseAlignment()
{
return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
}
// #pragma mark -
BTextControl::TextViewLayoutItem::TextViewLayoutItem(BTextControl* parent)
: fParent(parent),
fFrame()
{
// by default the part right of the divider shall have an unlimited maximum
// width
SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
}
bool
BTextControl::TextViewLayoutItem::IsVisible()
{
return !fParent->IsHidden(fParent);
}
void
BTextControl::TextViewLayoutItem::SetVisible(bool visible)
{
// not allowed
}
BRect
BTextControl::TextViewLayoutItem::Frame()
{
return fFrame;
}
void
BTextControl::TextViewLayoutItem::SetFrame(BRect frame)
{
fFrame = frame;
fParent->_UpdateFrame();
}
BView*
BTextControl::TextViewLayoutItem::View()
{
return fParent;
}
BSize
BTextControl::TextViewLayoutItem::BaseMinSize()
{
fParent->_ValidateLayoutData();
BSize size = fParent->fLayoutData->text_view_min;
size.width += 2 * kFrameMargin;
size.height += 2 * kFrameMargin;
return size;
}
BSize
BTextControl::TextViewLayoutItem::BaseMaxSize()
{
BSize size(BaseMinSize());
size.width = B_SIZE_UNLIMITED;
return size;
}
BSize
BTextControl::TextViewLayoutItem::BasePreferredSize()
{
BSize size(BaseMinSize());
// puh, no idea...
size.width = 100;
return size;
}
BAlignment
BTextControl::TextViewLayoutItem::BaseAlignment()
{
return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
}