2 * Copyright 2001-2015, Haiku Inc.
3 * Distributed under the terms of the MIT License.
6 * Frans van Nispen (xlr8@tref.nl)
7 * Stephan Aßmus <superstippi@gmx.de>
8 * Ingo Weinhold <bonefish@cs.tu-berlin.de>
12 /*! BTextControl displays text that can act like a control. */
15 #include <TextControl.h>
19 #include <AbstractLayoutItem.h>
20 #include <ControlLook.h>
21 #include <LayoutUtils.h>
23 #include <PropertyInfo.h>
27 #include <binary_compatibility/Interface.h>
28 #include <binary_compatibility/Support.h>
30 #include "TextInput.h"
33 //#define TRACE_TEXT_CONTROL
34 #ifdef TRACE_TEXT_CONTROL
36 # include <FunctionTracer.h>
37 static int32 sFunctionDepth
= -1;
38 # define CALLED(x...) FunctionTracer _ft("BTextControl", __FUNCTION__, \
40 # define TRACE(x...) { BString _to; \
41 _to.Append(' ', (sFunctionDepth + 1) * 2); \
42 printf("%s", _to.String()); printf(x); }
50 const char* const kFrameField
= "BTextControl:layoutitem:frame";
51 const char* const kTextViewItemField
= "BTextControl:textViewItem";
52 const char* const kLabelItemField
= "BMenuField:labelItem";
56 static property_info sPropertyList
[] = {
59 { B_GET_PROPERTY
, B_SET_PROPERTY
},
60 { B_DIRECT_SPECIFIER
},
69 class BTextControl::LabelLayoutItem
: public BAbstractLayoutItem
{
71 LabelLayoutItem(BTextControl
* parent
);
72 LabelLayoutItem(BMessage
* from
);
74 virtual bool IsVisible();
75 virtual void SetVisible(bool visible
);
77 virtual BRect
Frame();
78 virtual void SetFrame(BRect frame
);
80 void SetParent(BTextControl
* parent
);
81 virtual BView
* View();
83 virtual BSize
BaseMinSize();
84 virtual BSize
BaseMaxSize();
85 virtual BSize
BasePreferredSize();
86 virtual BAlignment
BaseAlignment();
88 BRect
FrameInParent() const;
90 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
91 static BArchivable
* Instantiate(BMessage
* from
);
94 BTextControl
* fParent
;
99 class BTextControl::TextViewLayoutItem
: public BAbstractLayoutItem
{
101 TextViewLayoutItem(BTextControl
* parent
);
102 TextViewLayoutItem(BMessage
* from
);
104 virtual bool IsVisible();
105 virtual void SetVisible(bool visible
);
107 virtual BRect
Frame();
108 virtual void SetFrame(BRect frame
);
110 void SetParent(BTextControl
* parent
);
111 virtual BView
* View();
113 virtual BSize
BaseMinSize();
114 virtual BSize
BaseMaxSize();
115 virtual BSize
BasePreferredSize();
116 virtual BAlignment
BaseAlignment();
118 BRect
FrameInParent() const;
120 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
121 static BArchivable
* Instantiate(BMessage
* from
);
123 BTextControl
* fParent
;
128 struct BTextControl::LayoutData
{
129 LayoutData(float width
, float height
)
131 label_layout_item(NULL
),
132 text_view_layout_item(NULL
),
133 previous_width(width
),
134 previous_height(height
),
139 LabelLayoutItem
* label_layout_item
;
140 TextViewLayoutItem
* text_view_layout_item
;
141 float previous_width
; // used in FrameResized() for
142 float previous_height
; // invalidation
143 font_height font_info
;
152 static const int32 kFrameMargin
= 2;
153 static const int32 kLabelInputSpacing
= 3;
156 // #pragma mark - BTextControl
159 BTextControl::BTextControl(BRect frame
, const char* name
, const char* label
,
160 const char* text
, BMessage
* message
, uint32 resizeMask
, uint32 flags
)
162 BControl(frame
, name
, label
, message
, resizeMask
, flags
| B_FRAME_EVENTS
)
170 BTextControl::BTextControl(const char* name
, const char* label
,
171 const char* text
, BMessage
* message
, uint32 flags
)
173 BControl(name
, label
, message
, flags
| B_FRAME_EVENTS
)
181 BTextControl::BTextControl(const char* label
, const char* text
,
184 BControl(NULL
, label
, message
,
185 B_WILL_DRAW
| B_NAVIGABLE
| B_FRAME_EVENTS
)
193 BTextControl::~BTextControl()
195 SetModificationMessage(NULL
);
200 // #pragma mark - Archiving
203 BTextControl::BTextControl(BMessage
* archive
)
205 BControl(BUnarchiver::PrepareArchive(archive
))
207 BUnarchiver
unarchiver(archive
);
209 _InitData(Label(), archive
);
211 if (!BUnarchiver::IsArchiveManaged(archive
))
212 _InitText(NULL
, archive
);
215 if (archive
->HasFloat("_divide"))
216 err
= archive
->FindFloat("_divide", &fDivider
);
218 if (err
== B_OK
&& archive
->HasMessage("_mod_msg")) {
219 BMessage
* message
= new BMessage
;
220 err
= archive
->FindMessage("_mod_msg", message
);
221 SetModificationMessage(message
);
224 unarchiver
.Finish(err
);
229 BTextControl::Instantiate(BMessage
* archive
)
231 if (validate_instantiation(archive
, "BTextControl"))
232 return new BTextControl(archive
);
239 BTextControl::Archive(BMessage
* data
, bool deep
) const
241 BArchiver
archiver(data
);
242 status_t result
= BControl::Archive(data
, deep
);
244 alignment labelAlignment
;
245 alignment textAlignment
;
247 GetAlignment(&labelAlignment
, &textAlignment
);
250 result
= data
->AddInt32("_a_label", labelAlignment
);
253 result
= data
->AddInt32("_a_text", textAlignment
);
256 result
= data
->AddFloat("_divide", Divider());
258 if (result
== B_OK
&& ModificationMessage() != NULL
)
259 result
= data
->AddMessage("_mod_msg", ModificationMessage());
261 return archiver
.Finish(result
);
266 BTextControl::AllArchived(BMessage
* into
) const
268 BArchiver
archiver(into
);
271 if (archiver
.IsArchived(fLayoutData
->text_view_layout_item
)) {
272 err
= archiver
.AddArchivable(kTextViewItemField
,
273 fLayoutData
->text_view_layout_item
);
276 if (err
== B_OK
&& archiver
.IsArchived(fLayoutData
->label_layout_item
)) {
277 err
= archiver
.AddArchivable(kLabelItemField
,
278 fLayoutData
->label_layout_item
);
286 BTextControl::AllUnarchived(const BMessage
* from
)
289 if ((err
= BControl::AllUnarchived(from
)) != B_OK
)
292 _InitText(NULL
, from
);
294 BUnarchiver
unarchiver(from
);
295 if (unarchiver
.IsInstantiated(kTextViewItemField
)) {
296 err
= unarchiver
.FindObject(kTextViewItemField
,
297 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
,
298 fLayoutData
->text_view_layout_item
);
301 fLayoutData
->text_view_layout_item
->SetParent(this);
306 if (unarchiver
.IsInstantiated(kLabelItemField
)) {
307 err
= unarchiver
.FindObject(kLabelItemField
,
308 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
,
309 fLayoutData
->label_layout_item
);
312 fLayoutData
->label_layout_item
->SetParent(this);
318 // #pragma mark - Hook methods
322 BTextControl::AllAttached()
324 BControl::AllAttached();
329 BTextControl::AllDetached()
331 BControl::AllDetached();
336 BTextControl::AttachedToWindow()
338 BControl::AttachedToWindow();
340 _UpdateTextViewColors(IsEnabled());
341 fText
->MakeEditable(IsEnabled());
346 BTextControl::DetachedFromWindow()
348 BControl::DetachedFromWindow();
353 BTextControl::Draw(BRect updateRect
)
355 bool enabled
= IsEnabled();
356 bool active
= fText
->IsFocus() && Window()->IsActive();
358 BRect rect
= fText
->Frame();
359 rect
.InsetBy(-2, -2);
361 rgb_color base
= ViewColor();
362 uint32 flags
= fLook
;
364 flags
|= BControlLook::B_DISABLED
;
367 flags
|= BControlLook::B_FOCUSED
;
369 be_control_look
->DrawTextControlBorder(this, rect
, updateRect
, base
,
372 if (Label() != NULL
) {
373 if (fLayoutData
->label_layout_item
!= NULL
) {
374 rect
= fLayoutData
->label_layout_item
->FrameInParent();
377 rect
.right
= fDivider
- kLabelInputSpacing
;
380 // erase the is control flag before drawing the label so that the label
381 // will get drawn using B_PANEL_TEXT_COLOR
382 flags
&= ~BControlLook::B_IS_CONTROL
;
384 be_control_look
->DrawLabel(this, Label(), rect
, updateRect
,
385 base
, flags
, BAlignment(fLabelAlign
, B_ALIGN_MIDDLE
));
391 BTextControl::FrameMoved(BPoint newPosition
)
393 BControl::FrameMoved(newPosition
);
398 BTextControl::FrameResized(float width
, float height
)
402 BControl::FrameResized(width
, height
);
404 // TODO: this causes flickering still...
408 BRect bounds
= Bounds();
410 if (bounds
.Width() > fLayoutData
->previous_width
) {
411 // invalidate the region between the old and the new right border
413 rect
.left
+= fLayoutData
->previous_width
- kFrameMargin
;
416 } else if (bounds
.Width() < fLayoutData
->previous_width
) {
417 // invalidate the region of the new right border
419 rect
.left
= rect
.right
- kFrameMargin
;
425 if (bounds
.Height() > fLayoutData
->previous_height
) {
426 // invalidate the region between the old and the new bottom border
428 rect
.top
+= fLayoutData
->previous_height
- kFrameMargin
;
431 // invalidate label area
433 rect
.right
= fDivider
;
435 } else if (bounds
.Height() < fLayoutData
->previous_height
) {
436 // invalidate the region of the new bottom border
438 rect
.top
= rect
.bottom
- kFrameMargin
;
440 // invalidate label area
442 rect
.right
= fDivider
;
446 fLayoutData
->previous_width
= bounds
.Width();
447 fLayoutData
->previous_height
= bounds
.Height();
449 TRACE("width: %.2f, height: %.2f\n", bounds
.Width(), bounds
.Height());
454 BTextControl::Invoke(BMessage
* message
)
456 return BControl::Invoke(message
);
461 BTextControl::LayoutInvalidated(bool descendants
)
465 fLayoutData
->valid
= false;
470 BTextControl::MessageReceived(BMessage
* message
)
472 if (message
->what
== B_COLORS_UPDATED
) {
474 if (message
->HasColor(ui_color_name(B_PANEL_BACKGROUND_COLOR
))
475 || message
->HasColor(ui_color_name(B_PANEL_TEXT_COLOR
))
476 || message
->HasColor(ui_color_name(B_DOCUMENT_BACKGROUND_COLOR
))
477 || message
->HasColor(ui_color_name(B_DOCUMENT_TEXT_COLOR
))) {
478 _UpdateTextViewColors(IsEnabled());
482 if (message
->what
== B_GET_PROPERTY
|| message
->what
== B_SET_PROPERTY
) {
483 BMessage
reply(B_REPLY
);
484 bool handled
= false;
489 const char* property
;
490 if (message
->GetCurrentSpecifier(&index
, &specifier
, &form
, &property
) == B_OK
) {
491 if (strcmp(property
, "Value") == 0) {
492 if (message
->what
== B_GET_PROPERTY
) {
493 reply
.AddString("result", fText
->Text());
496 const char* value
= NULL
;
498 if (message
->FindString("data", &value
) == B_OK
) {
499 fText
->SetText(value
);
500 reply
.AddInt32("error", B_OK
);
508 message
->SendReply(&reply
);
513 BControl::MessageReceived(message
);
518 BTextControl::MouseDown(BPoint where
)
520 if (!fText
->IsFocus())
521 fText
->MakeFocus(true);
526 BTextControl::MouseMoved(BPoint where
, uint32 transit
,
527 const BMessage
* dragMessage
)
529 BControl::MouseMoved(where
, transit
, dragMessage
);
534 BTextControl::MouseUp(BPoint where
)
536 BControl::MouseUp(where
);
541 BTextControl::WindowActivated(bool active
)
543 if (fText
->IsFocus()) {
544 // invalidate to remove/show focus indication
545 BRect rect
= fText
->Frame();
546 rect
.InsetBy(-1, -1);
549 // help out embedded text view which doesn't
550 // get notified of this
556 // #pragma mark - Getters and Setters
560 BTextControl::SetText(const char* text
)
562 if (InvokeKind() != B_CONTROL_INVOKED
)
567 fText
->SetText(text
);
569 if (fText
->IsFocus()) {
570 fText
->SetInitialText();
579 BTextControl::Text() const
581 return fText
->Text();
586 BTextControl::TextLength() const
588 return fText
->TextLength();
593 BTextControl::MarkAsInvalid(bool invalid
)
598 fLook
|= BControlLook::B_INVALID
;
600 fLook
&= ~BControlLook::B_INVALID
;
608 BTextControl::SetValue(int32 value
)
610 BControl::SetValue(value
);
615 BTextControl::TextView() const
622 BTextControl::SetModificationMessage(BMessage
* message
)
624 delete fModificationMessage
;
625 fModificationMessage
= message
;
630 BTextControl::ModificationMessage() const
632 return fModificationMessage
;
637 BTextControl::SetAlignment(alignment labelAlignment
, alignment textAlignment
)
639 fText
->SetAlignment(textAlignment
);
640 fText
->AlignTextRect();
642 if (fLabelAlign
!= labelAlignment
) {
643 fLabelAlign
= labelAlignment
;
650 BTextControl::GetAlignment(alignment
* _label
, alignment
* _text
) const
653 *_label
= fLabelAlign
;
656 *_text
= fText
->Alignment();
661 BTextControl::SetDivider(float position
)
663 fDivider
= floorf(position
+ 0.5);
675 BTextControl::Divider() const
682 BTextControl::MakeFocus(bool state
)
684 if (state
!= fText
->IsFocus()) {
685 fText
->MakeFocus(state
);
694 BTextControl::SetEnabled(bool enable
)
696 if (IsEnabled() == enable
)
699 if (Window() != NULL
) {
700 fText
->MakeEditable(enable
);
702 fText
->SetFlags(fText
->Flags() | B_NAVIGABLE
);
704 fText
->SetFlags(fText
->Flags() & ~B_NAVIGABLE
);
706 _UpdateTextViewColors(enable
);
709 Window()->UpdateIfNeeded();
712 BControl::SetEnabled(enable
);
717 BTextControl::GetPreferredSize(float* _width
, float* _height
)
721 _ValidateLayoutData();
724 float minWidth
= fLayoutData
->min
.width
;
725 if (Label() == NULL
&& !(Flags() & B_SUPPORTS_LAYOUT
)) {
726 // Indeed, only if there is no label! BeOS backwards compatible
728 minWidth
= max_c(minWidth
, Bounds().Width());
734 *_height
= fLayoutData
->min
.height
;
739 BTextControl::ResizeToPreferred()
741 BView::ResizeToPreferred();
744 const char* label
= Label();
746 fDivider
= ceil(StringWidth(label
)) + 2.0;
753 BTextControl::SetFlags(uint32 flags
)
755 // If the textview is navigable, set it to not navigable if needed
756 // Else if it is not navigable, set it to navigable if needed
757 if (fText
->Flags() & B_NAVIGABLE
) {
758 if (!(flags
& B_NAVIGABLE
))
759 fText
->SetFlags(fText
->Flags() & ~B_NAVIGABLE
);
762 if (flags
& B_NAVIGABLE
)
763 fText
->SetFlags(fText
->Flags() | B_NAVIGABLE
);
766 // Don't make this one navigable
767 flags
&= ~B_NAVIGABLE
;
769 BView::SetFlags(flags
);
773 // #pragma mark - Scripting
777 BTextControl::ResolveSpecifier(BMessage
* message
, int32 index
,
778 BMessage
* specifier
, int32 what
, const char* property
)
780 BPropertyInfo
propInfo(sPropertyList
);
782 if (propInfo
.FindMatch(message
, 0, specifier
, what
, property
) >= B_OK
)
785 return BControl::ResolveSpecifier(message
, index
, specifier
, what
,
791 BTextControl::GetSupportedSuites(BMessage
* data
)
793 return BControl::GetSupportedSuites(data
);
797 // #pragma mark - Layout
801 BTextControl::MinSize()
805 _ValidateLayoutData();
806 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData
->min
);
811 BTextControl::MaxSize()
815 _ValidateLayoutData();
817 BSize max
= fLayoutData
->min
;
818 max
.width
= B_SIZE_UNLIMITED
;
820 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max
);
825 BTextControl::PreferredSize()
829 _ValidateLayoutData();
830 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData
->min
);
835 BTextControl::LayoutAlignment()
839 _ValidateLayoutData();
840 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
841 BAlignment(B_ALIGN_LEFT
, B_ALIGN_VERTICAL_CENTER
));
846 BTextControl::CreateLabelLayoutItem()
848 if (!fLayoutData
->label_layout_item
)
849 fLayoutData
->label_layout_item
= new LabelLayoutItem(this);
851 return fLayoutData
->label_layout_item
;
856 BTextControl::CreateTextViewLayoutItem()
858 if (!fLayoutData
->text_view_layout_item
)
859 fLayoutData
->text_view_layout_item
= new TextViewLayoutItem(this);
861 return fLayoutData
->text_view_layout_item
;
866 BTextControl::DoLayout()
868 // Bail out, if we shan't do layout.
869 if (!(Flags() & B_SUPPORTS_LAYOUT
))
874 // If the user set a layout, we let the base class version call its
881 _ValidateLayoutData();
883 // validate current size
884 BSize
size(Bounds().Size());
885 if (size
.width
< fLayoutData
->min
.width
)
886 size
.width
= fLayoutData
->min
.width
;
888 if (size
.height
< fLayoutData
->min
.height
)
889 size
.height
= fLayoutData
->min
.height
;
891 BRect
dirty(fText
->Frame());
896 if (fLayoutData
->text_view_layout_item
!= NULL
) {
897 if (fLayoutData
->label_layout_item
!= NULL
) {
898 // We have layout items. They define the divider location.
899 divider
= fabs(fLayoutData
->text_view_layout_item
->Frame().left
900 - fLayoutData
->label_layout_item
->Frame().left
);
902 textFrame
= fLayoutData
->text_view_layout_item
->FrameInParent();
904 if (fLayoutData
->label_width
> 0) {
905 divider
= fLayoutData
->label_width
906 + be_control_look
->DefaultLabelSpacing();
908 textFrame
.Set(divider
, 0, size
.width
, size
.height
);
911 // place the text view and set the divider
912 textFrame
.InsetBy(kFrameMargin
, kFrameMargin
);
913 BLayoutUtils::AlignInFrame(fText
, textFrame
);
917 // invalidate dirty region
918 dirty
= dirty
| fText
->Frame();
919 dirty
.InsetBy(-kFrameMargin
, -kFrameMargin
);
925 // #pragma mark - protected methods
929 BTextControl::SetIcon(const BBitmap
* icon
, uint32 flags
)
931 return BControl::SetIcon(icon
, flags
);
935 // #pragma mark - private methods
939 BTextControl::Perform(perform_code code
, void* _data
)
942 case PERFORM_CODE_MIN_SIZE
:
943 ((perform_data_min_size
*)_data
)->return_value
944 = BTextControl::MinSize();
947 case PERFORM_CODE_MAX_SIZE
:
948 ((perform_data_max_size
*)_data
)->return_value
949 = BTextControl::MaxSize();
952 case PERFORM_CODE_PREFERRED_SIZE
:
953 ((perform_data_preferred_size
*)_data
)->return_value
954 = BTextControl::PreferredSize();
957 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
958 ((perform_data_layout_alignment
*)_data
)->return_value
959 = BTextControl::LayoutAlignment();
962 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
963 ((perform_data_has_height_for_width
*)_data
)->return_value
964 = BTextControl::HasHeightForWidth();
967 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
969 perform_data_get_height_for_width
* data
970 = (perform_data_get_height_for_width
*)_data
;
971 BTextControl::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
976 case PERFORM_CODE_SET_LAYOUT
:
978 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
979 BTextControl::SetLayout(data
->layout
);
983 case PERFORM_CODE_LAYOUT_INVALIDATED
:
985 perform_data_layout_invalidated
* data
986 = (perform_data_layout_invalidated
*)_data
;
987 BTextControl::LayoutInvalidated(data
->descendants
);
991 case PERFORM_CODE_DO_LAYOUT
:
993 BTextControl::DoLayout();
997 case PERFORM_CODE_SET_ICON
:
999 perform_data_set_icon
* data
= (perform_data_set_icon
*)_data
;
1000 return BTextControl::SetIcon(data
->icon
, data
->flags
);
1003 case PERFORM_CODE_ALL_UNARCHIVED
:
1005 perform_data_all_unarchived
* data
1006 = (perform_data_all_unarchived
*)_data
;
1007 data
->return_value
= BTextControl::AllUnarchived(data
->archive
);
1011 case PERFORM_CODE_ALL_ARCHIVED
:
1013 perform_data_all_archived
* data
1014 = (perform_data_all_archived
*)_data
;
1015 data
->return_value
= BTextControl::AllArchived(data
->archive
);
1020 return BControl::Perform(code
, _data
);
1024 // #pragma mark - FBC padding
1027 void BTextControl::_ReservedTextControl1() {}
1028 void BTextControl::_ReservedTextControl2() {}
1029 void BTextControl::_ReservedTextControl3() {}
1030 void BTextControl::_ReservedTextControl4() {}
1034 BTextControl::operator=(const BTextControl
&)
1041 BTextControl::_UpdateTextViewColors(bool enable
)
1043 rgb_color textColor
= ui_color(B_DOCUMENT_TEXT_COLOR
);
1044 rgb_color viewColor
= ui_color(B_DOCUMENT_BACKGROUND_COLOR
);
1047 fText
->GetFontAndColor(0, &font
);
1050 textColor
= disable_color(textColor
, ViewColor());
1051 viewColor
= disable_color(ViewColor(), viewColor
);
1054 fText
->SetFontAndColor(&font
, B_FONT_ALL
, &textColor
);
1055 fText
->SetViewColor(viewColor
);
1056 fText
->SetLowColor(viewColor
);
1061 BTextControl::_CommitValue()
1067 BTextControl::_InitData(const char* label
, const BMessage
* archive
)
1069 BRect
bounds(Bounds());
1072 fModificationMessage
= NULL
;
1073 fLabelAlign
= B_ALIGN_LEFT
;
1075 fLayoutData
= new LayoutData(bounds
.Width(), bounds
.Height());
1079 BFont
font(be_plain_font
);
1081 if (!archive
|| !archive
->HasString("_fname"))
1082 flags
|= B_FONT_FAMILY_AND_STYLE
;
1084 if (!archive
|| !archive
->HasFloat("_fflt"))
1085 flags
|= B_FONT_SIZE
;
1088 SetFont(&font
, flags
);
1091 fDivider
= floorf(bounds
.Width() / 2.0f
);
1098 BTextControl::_InitText(const char* initialText
, const BMessage
* archive
)
1101 fText
= static_cast<BPrivate::_BTextInput_
*>(FindView("_input_"));
1103 if (fText
== NULL
) {
1104 BRect
bounds(Bounds());
1105 BRect
frame(fDivider
, bounds
.top
, bounds
.right
, bounds
.bottom
);
1106 // we are stroking the frame around the text view, which
1108 frame
.InsetBy(kFrameMargin
, kFrameMargin
);
1109 BRect
textRect(frame
.OffsetToCopy(B_ORIGIN
));
1111 fText
= new BPrivate::_BTextInput_(frame
, textRect
,
1112 B_FOLLOW_ALL
, B_WILL_DRAW
| B_FRAME_EVENTS
1113 | (Flags() & B_NAVIGABLE
));
1116 SetText(initialText
);
1117 fText
->SetAlignment(B_ALIGN_LEFT
);
1118 fText
->AlignTextRect();
1121 // Although this is not strictly initializing the text view,
1122 // it cannot be done while fText is NULL, so it resides here.
1124 int32 labelAlignment
= B_ALIGN_LEFT
;
1125 int32 textAlignment
= B_ALIGN_LEFT
;
1127 status_t err
= B_OK
;
1128 if (archive
->HasInt32("_a_label"))
1129 err
= archive
->FindInt32("_a_label", &labelAlignment
);
1131 if (err
== B_OK
&& archive
->HasInt32("_a_text"))
1132 err
= archive
->FindInt32("_a_text", &textAlignment
);
1134 SetAlignment((alignment
)labelAlignment
, (alignment
)textAlignment
);
1137 uint32 navigableFlags
= Flags() & B_NAVIGABLE
;
1138 if (navigableFlags
!= 0)
1139 BView::SetFlags(Flags() & ~B_NAVIGABLE
);
1144 BTextControl::_ValidateLayout()
1148 _ValidateLayoutData();
1150 ResizeTo(Bounds().Width(), fLayoutData
->min
.height
);
1157 BTextControl::_LayoutTextView()
1162 if (fLayoutData
->text_view_layout_item
!= NULL
) {
1163 frame
= fLayoutData
->text_view_layout_item
->FrameInParent();
1166 frame
.left
= fDivider
;
1169 // we are stroking the frame around the text view, which
1171 frame
.InsetBy(kFrameMargin
, kFrameMargin
);
1172 fText
->MoveTo(frame
.left
, frame
.top
);
1173 fText
->ResizeTo(frame
.Width(), frame
.Height());
1174 fText
->AlignTextRect();
1176 TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
1177 TRACE("fDivider: %.2f\n", fDivider
);
1178 TRACE("fText frame: (%.2f, %.2f, %.2f, %.2f)\n",
1179 frame
.left
, frame
.top
, frame
.right
, frame
.bottom
);
1184 BTextControl::_UpdateFrame()
1188 if (fLayoutData
->text_view_layout_item
!= NULL
) {
1189 BRect textFrame
= fLayoutData
->text_view_layout_item
->Frame();
1191 if (fLayoutData
->label_layout_item
!= NULL
)
1192 labelFrame
= fLayoutData
->label_layout_item
->Frame();
1195 if (labelFrame
.IsValid()) {
1196 frame
= textFrame
| labelFrame
;
1199 fDivider
= fabs(textFrame
.left
- labelFrame
.left
);
1206 MoveTo(frame
.left
, frame
.top
);
1207 BSize
oldSize(Bounds().Size());
1208 ResizeTo(frame
.Width(), frame
.Height());
1209 BSize
newSize(Bounds().Size());
1211 // If the size changes, ResizeTo() will trigger a relayout, otherwise
1212 // we need to do that explicitly.
1213 if (newSize
!= oldSize
)
1220 BTextControl::_ValidateLayoutData()
1224 if (fLayoutData
->valid
)
1227 // cache font height
1228 font_height
& fh
= fLayoutData
->font_info
;
1231 const char* label
= Label();
1232 if (label
!= NULL
) {
1233 fLayoutData
->label_width
= ceilf(StringWidth(label
));
1234 fLayoutData
->label_height
= ceilf(fh
.ascent
) + ceilf(fh
.descent
);
1236 fLayoutData
->label_width
= 0;
1237 fLayoutData
->label_height
= 0;
1240 // compute the minimal divider
1242 if (fLayoutData
->label_width
> 0) {
1243 divider
= fLayoutData
->label_width
1244 + be_control_look
->DefaultLabelSpacing();
1247 // If we shan't do real layout, we let the current divider take influence.
1248 if (!(Flags() & B_SUPPORTS_LAYOUT
))
1249 divider
= max_c(divider
, fDivider
);
1251 // get the minimal (== preferred) text view size
1252 fLayoutData
->text_view_min
= fText
->MinSize();
1254 TRACE("text view min width: %.2f\n", fLayoutData
->text_view_min
.width
);
1256 // compute our minimal (== preferred) size
1257 BSize
min(fLayoutData
->text_view_min
);
1258 min
.width
+= 2 * kFrameMargin
;
1259 min
.height
+= 2 * kFrameMargin
;
1262 min
.width
+= divider
;
1264 if (fLayoutData
->label_height
> min
.height
)
1265 min
.height
= fLayoutData
->label_height
;
1267 fLayoutData
->min
= min
;
1269 fLayoutData
->valid
= true;
1270 ResetLayoutInvalidation();
1272 TRACE("width: %.2f, height: %.2f\n", min
.width
, min
.height
);
1276 // #pragma mark - BTextControl::LabelLayoutItem
1279 BTextControl::LabelLayoutItem::LabelLayoutItem(BTextControl
* parent
)
1287 BTextControl::LabelLayoutItem::LabelLayoutItem(BMessage
* from
)
1289 BAbstractLayoutItem(from
),
1293 from
->FindRect(kFrameField
, &fFrame
);
1298 BTextControl::LabelLayoutItem::IsVisible()
1300 return !fParent
->IsHidden(fParent
);
1305 BTextControl::LabelLayoutItem::SetVisible(bool visible
)
1312 BTextControl::LabelLayoutItem::Frame()
1319 BTextControl::LabelLayoutItem::SetFrame(BRect frame
)
1322 fParent
->_UpdateFrame();
1327 BTextControl::LabelLayoutItem::SetParent(BTextControl
* parent
)
1334 BTextControl::LabelLayoutItem::View()
1341 BTextControl::LabelLayoutItem::BaseMinSize()
1343 fParent
->_ValidateLayoutData();
1345 if (!fParent
->Label())
1346 return BSize(-1, -1);
1348 return BSize(fParent
->fLayoutData
->label_width
1349 + be_control_look
->DefaultLabelSpacing(),
1350 fParent
->fLayoutData
->label_height
);
1355 BTextControl::LabelLayoutItem::BaseMaxSize()
1357 return BaseMinSize();
1362 BTextControl::LabelLayoutItem::BasePreferredSize()
1364 return BaseMinSize();
1369 BTextControl::LabelLayoutItem::BaseAlignment()
1371 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
1376 BTextControl::LabelLayoutItem::FrameInParent() const
1378 return fFrame
.OffsetByCopy(-fParent
->Frame().left
, -fParent
->Frame().top
);
1383 BTextControl::LabelLayoutItem::Archive(BMessage
* into
, bool deep
) const
1385 BArchiver
archiver(into
);
1386 status_t err
= BAbstractLayoutItem::Archive(into
, deep
);
1388 err
= into
->AddRect(kFrameField
, fFrame
);
1390 return archiver
.Finish(err
);
1395 BTextControl::LabelLayoutItem::Instantiate(BMessage
* from
)
1397 if (validate_instantiation(from
, "BTextControl::LabelLayoutItem"))
1398 return new LabelLayoutItem(from
);
1403 // #pragma mark - BTextControl::TextViewLayoutItem
1406 BTextControl::TextViewLayoutItem::TextViewLayoutItem(BTextControl
* parent
)
1411 // by default the part right of the divider shall have an unlimited maximum
1413 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
1417 BTextControl::TextViewLayoutItem::TextViewLayoutItem(BMessage
* from
)
1419 BAbstractLayoutItem(from
),
1423 from
->FindRect(kFrameField
, &fFrame
);
1428 BTextControl::TextViewLayoutItem::IsVisible()
1430 return !fParent
->IsHidden(fParent
);
1435 BTextControl::TextViewLayoutItem::SetVisible(bool visible
)
1442 BTextControl::TextViewLayoutItem::Frame()
1449 BTextControl::TextViewLayoutItem::SetFrame(BRect frame
)
1452 fParent
->_UpdateFrame();
1457 BTextControl::TextViewLayoutItem::SetParent(BTextControl
* parent
)
1464 BTextControl::TextViewLayoutItem::View()
1471 BTextControl::TextViewLayoutItem::BaseMinSize()
1473 fParent
->_ValidateLayoutData();
1475 BSize size
= fParent
->fLayoutData
->text_view_min
;
1476 size
.width
+= 2 * kFrameMargin
;
1477 size
.height
+= 2 * kFrameMargin
;
1484 BTextControl::TextViewLayoutItem::BaseMaxSize()
1486 BSize
size(BaseMinSize());
1487 size
.width
= B_SIZE_UNLIMITED
;
1494 BTextControl::TextViewLayoutItem::BasePreferredSize()
1496 BSize
size(BaseMinSize());
1505 BTextControl::TextViewLayoutItem::BaseAlignment()
1507 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
1512 BTextControl::TextViewLayoutItem::FrameInParent() const
1514 return fFrame
.OffsetByCopy(-fParent
->Frame().left
, -fParent
->Frame().top
);
1519 BTextControl::TextViewLayoutItem::Archive(BMessage
* into
, bool deep
) const
1521 BArchiver
archiver(into
);
1522 status_t err
= BAbstractLayoutItem::Archive(into
, deep
);
1524 err
= into
->AddRect(kFrameField
, fFrame
);
1526 return archiver
.Finish(err
);
1531 BTextControl::TextViewLayoutItem::Instantiate(BMessage
* from
)
1533 if (validate_instantiation(from
, "BTextControl::TextViewLayoutItem"))
1534 return new TextViewLayoutItem(from
);
1541 B_IF_GCC_2(InvalidateLayout__12BTextControlb
,
1542 _ZN12BTextControl16InvalidateLayoutEb
)(BView
* view
, bool descendants
)
1544 perform_data_layout_invalidated data
;
1545 data
.descendants
= descendants
;
1547 view
->Perform(PERFORM_CODE_LAYOUT_INVALIDATED
, &data
);