2 * Copyright 2001-2013, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
6 * Stephan Aßmus, superstippi@gmx.de
7 * Marc Flerackers, mflerackers@androme.be
8 * John Scipione, jscipione@gmail.com
9 * Ingo Weinhold, bonefish@cs.tu-berlin.de
13 #include <MenuField.h>
17 // for printf in TRACE
21 #include <AbstractLayoutItem.h>
22 #include <BMCPrivate.h>
23 #include <ControlLook.h>
24 #include <LayoutUtils.h>
27 #include <MenuPrivate.h>
29 #include <MessageFilter.h>
33 #include <binary_compatibility/Interface.h>
34 #include <binary_compatibility/Support.h>
44 //#define TRACE_MENU_FIELD
45 #ifdef TRACE_MENU_FIELD
46 # include <FunctionTracer.h>
47 static int32 sFunctionDepth
= -1;
48 # define CALLED(x...) FunctionTracer _ft("BMenuField", __FUNCTION__, \
50 # define TRACE(x...) { BString _to; \
51 _to.Append(' ', (sFunctionDepth + 1) * 2); \
52 printf("%s", _to.String()); printf(x); }
59 static const float kMinMenuBarWidth
= 20.0f
;
60 // found by experimenting on BeOS R5
64 const char* const kFrameField
= "BMenuField:layoutItem:frame";
65 const char* const kMenuBarItemField
= "BMenuField:barItem";
66 const char* const kLabelItemField
= "BMenuField:labelItem";
70 // #pragma mark - LabelLayoutItem
73 class BMenuField::LabelLayoutItem
: public BAbstractLayoutItem
{
75 LabelLayoutItem(BMenuField
* parent
);
76 LabelLayoutItem(BMessage
* archive
);
78 virtual bool IsVisible();
79 virtual void SetVisible(bool visible
);
81 virtual BRect
Frame();
82 virtual void SetFrame(BRect frame
);
84 void SetParent(BMenuField
* parent
);
85 virtual BView
* View();
87 virtual BSize
BaseMinSize();
88 virtual BSize
BaseMaxSize();
89 virtual BSize
BasePreferredSize();
90 virtual BAlignment
BaseAlignment();
92 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
93 static BArchivable
* Instantiate(BMessage
* from
);
101 // #pragma mark - MenuBarLayoutItem
104 class BMenuField::MenuBarLayoutItem
: public BAbstractLayoutItem
{
106 MenuBarLayoutItem(BMenuField
* parent
);
107 MenuBarLayoutItem(BMessage
* from
);
109 virtual bool IsVisible();
110 virtual void SetVisible(bool visible
);
112 virtual BRect
Frame();
113 virtual void SetFrame(BRect frame
);
115 void SetParent(BMenuField
* parent
);
116 virtual BView
* View();
118 virtual BSize
BaseMinSize();
119 virtual BSize
BaseMaxSize();
120 virtual BSize
BasePreferredSize();
121 virtual BAlignment
BaseAlignment();
123 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
124 static BArchivable
* Instantiate(BMessage
* from
);
132 // #pragma mark - LayoutData
135 struct BMenuField::LayoutData
{
138 label_layout_item(NULL
),
139 menu_bar_layout_item(NULL
),
145 LabelLayoutItem
* label_layout_item
;
146 MenuBarLayoutItem
* menu_bar_layout_item
;
147 float previous_height
; // used in FrameResized() for
149 font_height font_info
;
158 // #pragma mark - MouseDownFilter
161 class MouseDownFilter
: public BMessageFilter
165 virtual ~MouseDownFilter();
167 virtual filter_result
Filter(BMessage
* message
, BHandler
** target
);
171 MouseDownFilter::MouseDownFilter()
173 BMessageFilter(B_ANY_DELIVERY
, B_ANY_SOURCE
)
178 MouseDownFilter::~MouseDownFilter()
184 MouseDownFilter::Filter(BMessage
* message
, BHandler
** target
)
186 return message
->what
== B_MOUSE_DOWN
? B_SKIP_MESSAGE
: B_DISPATCH_MESSAGE
;
190 // #pragma mark - BMenuField
193 using BPrivate::MenuPrivate
;
195 BMenuField::BMenuField(BRect frame
, const char* name
, const char* label
,
196 BMenu
* menu
, uint32 resizingMode
, uint32 flags
)
198 BView(frame
, name
, resizingMode
, flags
)
202 TRACE("frame.width: %.2f, height: %.2f\n", frame
.Width(), frame
.Height());
206 frame
.OffsetTo(B_ORIGIN
);
207 _InitMenuBar(menu
, frame
, false);
213 BMenuField::BMenuField(BRect frame
, const char* name
, const char* label
,
214 BMenu
* menu
, bool fixedSize
, uint32 resizingMode
, uint32 flags
)
216 BView(frame
, name
, resizingMode
, flags
)
220 fFixedSizeMB
= fixedSize
;
222 frame
.OffsetTo(B_ORIGIN
);
223 _InitMenuBar(menu
, frame
, fixedSize
);
229 BMenuField::BMenuField(const char* name
, const char* label
, BMenu
* menu
,
232 BView(name
, flags
| B_FRAME_EVENTS
)
236 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
242 BMenuField::BMenuField(const char* label
, BMenu
* menu
, uint32 flags
)
244 BView(NULL
, flags
| B_FRAME_EVENTS
)
248 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
254 //! Copy&Paste error, should be removed at some point (already private)
255 BMenuField::BMenuField(const char* name
, const char* label
, BMenu
* menu
,
256 BMessage
* message
, uint32 flags
)
258 BView(name
, flags
| B_FRAME_EVENTS
)
262 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
268 //! Copy&Paste error, should be removed at some point (already private)
269 BMenuField::BMenuField(const char* label
, BMenu
* menu
, BMessage
* message
)
271 BView(NULL
, B_WILL_DRAW
| B_NAVIGABLE
| B_FRAME_EVENTS
)
275 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
281 BMenuField::BMenuField(BMessage
* data
)
283 BView(BUnarchiver::PrepareArchive(data
))
285 BUnarchiver
unarchiver(data
);
286 const char* label
= NULL
;
287 data
->FindString("_label", &label
);
291 data
->FindFloat("_divide", &fDivider
);
294 if (data
->FindInt32("_align", &align
) == B_OK
)
295 SetAlignment((alignment
)align
);
297 if (!BUnarchiver::IsArchiveManaged(data
))
303 BMenuField::~BMenuField()
308 if (fMenuTaskID
>= 0)
309 wait_for_thread(fMenuTaskID
, &dummy
);
312 delete fMouseDownFilter
;
317 BMenuField::Instantiate(BMessage
* data
)
319 if (validate_instantiation(data
, "BMenuField"))
320 return new BMenuField(data
);
327 BMenuField::Archive(BMessage
* data
, bool deep
) const
329 BArchiver
archiver(data
);
330 status_t ret
= BView::Archive(data
, deep
);
332 if (ret
== B_OK
&& Label())
333 ret
= data
->AddString("_label", Label());
335 if (ret
== B_OK
&& !IsEnabled())
336 ret
= data
->AddBool("_disable", true);
339 ret
= data
->AddInt32("_align", Alignment());
341 ret
= data
->AddFloat("_divide", Divider());
343 if (ret
== B_OK
&& fFixedSizeMB
)
344 ret
= data
->AddBool("be:fixeds", true);
347 if (_BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
))
348 dmark
= menuBar
->IsPopUpMarkerShown();
350 data
->AddBool("be:dmark", dmark
);
352 return archiver
.Finish(ret
);
357 BMenuField::AllArchived(BMessage
* into
) const
360 if ((err
= BView::AllArchived(into
)) != B_OK
)
363 BArchiver
archiver(into
);
365 BArchivable
* menuBarItem
= fLayoutData
->menu_bar_layout_item
;
366 if (archiver
.IsArchived(menuBarItem
))
367 err
= archiver
.AddArchivable(kMenuBarItemField
, menuBarItem
);
372 BArchivable
* labelBarItem
= fLayoutData
->label_layout_item
;
373 if (archiver
.IsArchived(labelBarItem
))
374 err
= archiver
.AddArchivable(kLabelItemField
, labelBarItem
);
381 BMenuField::AllUnarchived(const BMessage
* from
)
383 BUnarchiver
unarchiver(from
);
386 if ((err
= BView::AllUnarchived(from
)) != B_OK
)
391 if (unarchiver
.IsInstantiated(kMenuBarItemField
)) {
392 MenuBarLayoutItem
*& menuItem
= fLayoutData
->menu_bar_layout_item
;
393 err
= unarchiver
.FindObject(kMenuBarItemField
,
394 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
, menuItem
);
397 menuItem
->SetParent(this);
402 if (unarchiver
.IsInstantiated(kLabelItemField
)) {
403 LabelLayoutItem
*& labelItem
= fLayoutData
->label_layout_item
;
404 err
= unarchiver
.FindObject(kLabelItemField
,
405 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
, labelItem
);
408 labelItem
->SetParent(this);
416 BMenuField::Draw(BRect updateRect
)
418 _DrawLabel(updateRect
);
419 _DrawMenuBar(updateRect
);
424 BMenuField::AttachedToWindow()
429 BView
* parent
= Parent();
430 if (parent
!= NULL
) {
431 // inherit the color from parent
432 color
= parent
->ViewColor();
433 if (color
== B_TRANSPARENT_COLOR
)
434 color
= ui_color(B_PANEL_BACKGROUND_COLOR
);
436 color
= ui_color(B_PANEL_BACKGROUND_COLOR
);
444 BMenuField::AllAttached()
448 TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
450 float width
= Bounds().Width();
451 if (!fFixedSizeMB
&& _MenuBarWidth() < kMinMenuBarWidth
) {
452 // The menu bar is too narrow, resize it to fit the menu items
453 BMenuItem
* item
= fMenuBar
->ItemAt(0);
456 fMenuBar
->GetItemMargins(NULL
, NULL
, &right
, NULL
);
457 width
= item
->Frame().Width() + kVMargin
+ _MenuBarOffset() + right
;
461 ResizeTo(width
, fMenuBar
->Bounds().Height() + kVMargin
* 2);
463 TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
468 BMenuField::MouseDown(BPoint where
)
470 BRect bounds
= fMenuBar
->ConvertFromParent(Bounds());
472 fMenuBar
->StartMenuBar(-1, false, true, &bounds
);
474 fMenuTaskID
= spawn_thread((thread_func
)_thread_entry
,
475 "_m_task_", B_NORMAL_PRIORITY
, this);
476 if (fMenuTaskID
>= 0 && resume_thread(fMenuTaskID
) == B_OK
) {
477 if (fMouseDownFilter
->Looper() == NULL
)
478 Window()->AddCommonFilter(fMouseDownFilter
);
480 MouseDownThread
<BMenuField
>::TrackMouse(this, &BMenuField::_DoneTracking
,
481 &BMenuField::_Track
);
487 BMenuField::KeyDown(const char* bytes
, int32 numBytes
)
497 BRect bounds
= fMenuBar
->ConvertFromParent(Bounds());
499 fMenuBar
->StartMenuBar(0, true, true, &bounds
);
502 bounds
.right
= fDivider
;
508 BView::KeyDown(bytes
, numBytes
);
514 BMenuField::MakeFocus(bool focused
)
516 if (IsFocus() == focused
)
519 BView::MakeFocus(focused
);
521 if (Window() != NULL
)
522 Invalidate(); // TODO: use fLayoutData->label_width
527 BMenuField::MessageReceived(BMessage
* message
)
529 BView::MessageReceived(message
);
534 BMenuField::WindowActivated(bool active
)
536 BView::WindowActivated(active
);
544 BMenuField::MouseMoved(BPoint point
, uint32 code
, const BMessage
* message
)
546 BView::MouseMoved(point
, code
, message
);
551 BMenuField::MouseUp(BPoint where
)
553 BView::MouseUp(where
);
558 BMenuField::DetachedFromWindow()
560 BView::DetachedFromWindow();
565 BMenuField::AllDetached()
567 BView::AllDetached();
572 BMenuField::FrameMoved(BPoint newPosition
)
574 BView::FrameMoved(newPosition
);
579 BMenuField::FrameResized(float newWidth
, float newHeight
)
581 BView::FrameResized(newWidth
, newHeight
);
584 // we have let the menubar resize itself, but
585 // in fixed size mode, the menubar is supposed to
586 // be at the right end of the view always. Since
587 // the menu bar is in follow left/right mode then,
588 // resizing ourselfs might have caused the menubar
590 fMenuBar
->ResizeTo(_MenuBarWidth(), fMenuBar
->Frame().Height());
593 if (newHeight
!= fLayoutData
->previous_height
&& Label()) {
594 // The height changed, which means the label has to move and we
595 // probably also invalidate a part of the borders around the menu bar.
596 // So don't be shy and invalidate the whole thing.
600 fLayoutData
->previous_height
= newHeight
;
605 BMenuField::Menu() const
612 BMenuField::MenuBar() const
619 BMenuField::MenuItem() const
621 return fMenuBar
->ItemAt(0);
626 BMenuField::SetLabel(const char* label
)
629 if (label
&& strcmp(fLabel
, label
) == 0)
635 fLabel
= strdup(label
);
645 BMenuField::Label() const
652 BMenuField::SetEnabled(bool on
)
658 fMenuBar
->SetEnabled(on
);
661 fMenuBar
->Invalidate(fMenuBar
->Bounds());
662 Invalidate(Bounds());
668 BMenuField::IsEnabled() const
675 BMenuField::SetAlignment(alignment label
)
682 BMenuField::Alignment() const
689 BMenuField::SetDivider(float position
)
691 position
= roundf(position
);
693 float delta
= fDivider
- position
;
699 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0) {
700 // We should never get here, since layout support means, we also
701 // layout the divider, and don't use this method at all.
704 BRect
dirty(fMenuBar
->Frame());
706 fMenuBar
->MoveTo(_MenuBarOffset(), kVMargin
);
709 fMenuBar
->ResizeTo(_MenuBarWidth(), dirty
.Height());
711 dirty
= dirty
| fMenuBar
->Frame();
712 dirty
.InsetBy(-kVMargin
, -kVMargin
);
720 BMenuField::Divider() const
727 BMenuField::ShowPopUpMarker()
729 if (_BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
)) {
730 menuBar
->TogglePopUpMarker(true);
731 menuBar
->Invalidate();
737 BMenuField::HidePopUpMarker()
739 if (_BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
)) {
740 menuBar
->TogglePopUpMarker(false);
741 menuBar
->Invalidate();
747 BMenuField::ResolveSpecifier(BMessage
* message
, int32 index
,
748 BMessage
* specifier
, int32 form
, const char* property
)
750 return BView::ResolveSpecifier(message
, index
, specifier
, form
, property
);
755 BMenuField::GetSupportedSuites(BMessage
* data
)
757 return BView::GetSupportedSuites(data
);
762 BMenuField::ResizeToPreferred()
766 TRACE("fMenuBar->Frame().width: %.2f, height: %.2f\n",
767 fMenuBar
->Frame().Width(), fMenuBar
->Frame().Height());
769 fMenuBar
->ResizeToPreferred();
771 TRACE("fMenuBar->Frame().width: %.2f, height: %.2f\n",
772 fMenuBar
->Frame().Width(), fMenuBar
->Frame().Height());
774 BView::ResizeToPreferred();
781 BMenuField::GetPreferredSize(float* _width
, float* _height
)
785 _ValidateLayoutData();
788 *_width
= fLayoutData
->min
.width
;
791 *_height
= fLayoutData
->min
.height
;
796 BMenuField::MinSize()
800 _ValidateLayoutData();
801 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData
->min
);
806 BMenuField::MaxSize()
810 _ValidateLayoutData();
812 BSize max
= fLayoutData
->min
;
813 max
.width
= B_SIZE_UNLIMITED
;
815 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max
);
820 BMenuField::PreferredSize()
824 _ValidateLayoutData();
825 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData
->min
);
830 BMenuField::CreateLabelLayoutItem()
832 if (fLayoutData
->label_layout_item
== NULL
)
833 fLayoutData
->label_layout_item
= new LabelLayoutItem(this);
835 return fLayoutData
->label_layout_item
;
840 BMenuField::CreateMenuBarLayoutItem()
842 if (fLayoutData
->menu_bar_layout_item
== NULL
) {
843 // align the menu bar in the full available space
844 fMenuBar
->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH
,
845 B_ALIGN_VERTICAL_UNSET
));
846 fLayoutData
->menu_bar_layout_item
= new MenuBarLayoutItem(this);
849 return fLayoutData
->menu_bar_layout_item
;
854 BMenuField::Perform(perform_code code
, void* _data
)
857 case PERFORM_CODE_MIN_SIZE
:
858 ((perform_data_min_size
*)_data
)->return_value
859 = BMenuField::MinSize();
862 case PERFORM_CODE_MAX_SIZE
:
863 ((perform_data_max_size
*)_data
)->return_value
864 = BMenuField::MaxSize();
867 case PERFORM_CODE_PREFERRED_SIZE
:
868 ((perform_data_preferred_size
*)_data
)->return_value
869 = BMenuField::PreferredSize();
872 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
873 ((perform_data_layout_alignment
*)_data
)->return_value
874 = BMenuField::LayoutAlignment();
877 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
878 ((perform_data_has_height_for_width
*)_data
)->return_value
879 = BMenuField::HasHeightForWidth();
882 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
884 perform_data_get_height_for_width
* data
885 = (perform_data_get_height_for_width
*)_data
;
886 BMenuField::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
891 case PERFORM_CODE_SET_LAYOUT
:
893 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
894 BMenuField::SetLayout(data
->layout
);
898 case PERFORM_CODE_LAYOUT_INVALIDATED
:
900 perform_data_layout_invalidated
* data
901 = (perform_data_layout_invalidated
*)_data
;
902 BMenuField::LayoutInvalidated(data
->descendants
);
906 case PERFORM_CODE_DO_LAYOUT
:
908 BMenuField::DoLayout();
912 case PERFORM_CODE_ALL_UNARCHIVED
:
914 perform_data_all_unarchived
* data
915 = (perform_data_all_unarchived
*)_data
;
916 data
->return_value
= BMenuField::AllUnarchived(data
->archive
);
920 case PERFORM_CODE_ALL_ARCHIVED
:
922 perform_data_all_archived
* data
923 = (perform_data_all_archived
*)_data
;
924 data
->return_value
= BMenuField::AllArchived(data
->archive
);
929 return BView::Perform(code
, _data
);
934 BMenuField::LayoutInvalidated(bool descendants
)
938 fLayoutData
->valid
= false;
943 BMenuField::DoLayout()
945 // Bail out, if we shan't do layout.
946 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
951 // If the user set a layout, we let the base class version call its
958 _ValidateLayoutData();
960 // validate current size
961 BSize
size(Bounds().Size());
962 if (size
.width
< fLayoutData
->min
.width
)
963 size
.width
= fLayoutData
->min
.width
;
965 if (size
.height
< fLayoutData
->min
.height
)
966 size
.height
= fLayoutData
->min
.height
;
970 if (fLayoutData
->label_layout_item
!= NULL
971 && fLayoutData
->menu_bar_layout_item
!= NULL
972 && fLayoutData
->label_layout_item
->Frame().IsValid()
973 && fLayoutData
->menu_bar_layout_item
->Frame().IsValid()) {
974 // We have valid layout items, they define the divider location.
975 divider
= fLayoutData
->menu_bar_layout_item
->Frame().left
976 - fLayoutData
->label_layout_item
->Frame().left
;
977 } else if (fLayoutData
->label_width
> 0) {
978 divider
= fLayoutData
->label_width
979 + be_control_look
->DefaultLabelSpacing();
983 BRect
dirty(fMenuBar
->Frame());
984 BRect
menuBarFrame(divider
+ kVMargin
, kVMargin
, size
.width
- kVMargin
,
985 size
.height
- kVMargin
);
987 // place the menu bar and set the divider
988 BLayoutUtils::AlignInFrame(fMenuBar
, menuBarFrame
);
992 // invalidate dirty region
993 dirty
= dirty
| fMenuBar
->Frame();
994 dirty
.InsetBy(-kVMargin
, -kVMargin
);
1000 void BMenuField::_ReservedMenuField1() {}
1001 void BMenuField::_ReservedMenuField2() {}
1002 void BMenuField::_ReservedMenuField3() {}
1006 BMenuField::InitObject(const char* label
)
1013 fAlign
= B_ALIGN_LEFT
;
1015 fFixedSizeMB
= false;
1017 fLayoutData
= new LayoutData
;
1018 fMouseDownFilter
= new MouseDownFilter();
1023 fDivider
= floorf(Frame().Width() / 2.0f
);
1030 BMenuField::InitObject2()
1034 if (!fFixedSizeMB
) {
1036 fMenuBar
->GetPreferredSize(NULL
, &height
);
1037 fMenuBar
->ResizeTo(_MenuBarWidth(), height
);
1040 TRACE("frame(%.1f, %.1f, %.1f, %.1f) (%.2f, %.2f)\n",
1041 fMenuBar
->Frame().left
, fMenuBar
->Frame().top
,
1042 fMenuBar
->Frame().right
, fMenuBar
->Frame().bottom
,
1043 fMenuBar
->Frame().Width(), fMenuBar
->Frame().Height());
1045 fMenuBar
->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN
));
1050 BMenuField::_DrawLabel(BRect updateRect
)
1054 BRect
rect(Bounds());
1055 rect
.right
= fDivider
;
1056 if (!rect
.IsValid() || !rect
.Intersects(updateRect
))
1059 _ValidateLayoutData();
1060 font_height
& fh
= fLayoutData
->font_info
;
1062 const char* label
= Label();
1066 // horizontal alignment
1070 x
= fDivider
- fLayoutData
->label_width
- 3.0f
;
1073 case B_ALIGN_CENTER
:
1074 x
= fDivider
- roundf(fLayoutData
->label_width
/ 2.0f
);
1082 // vertical alignment
1084 + roundf((rect
.Height() + 1 - fh
.ascent
- fh
.descent
) / 2.0f
)
1087 const rgb_color lowColor
= LowColor();
1089 MenuPrivate
menuPrivate(fMenuBar
);
1090 if (menuPrivate
.State() != MENU_STATE_CLOSED
)
1091 SetLowColor(ui_color(B_MENU_SELECTED_BACKGROUND_COLOR
));
1093 BRect
fillRect(rect
.InsetByCopy(0, kVMargin
));
1094 fillRect
.right
-= kVMargin
* 2;
1095 FillRect(fillRect
, B_SOLID_LOW
);
1099 flags
|= BControlLook::B_DISABLED
;
1101 be_control_look
->DrawLabel(this, label
, LowColor(), flags
, BPoint(x
, y
));
1103 SetLowColor(lowColor
);
1108 BMenuField::_DrawMenuBar(BRect updateRect
)
1112 BRect
rect(fMenuBar
->Frame().InsetByCopy(-kVMargin
, -kVMargin
));
1113 if (!rect
.IsValid() || !rect
.Intersects(updateRect
))
1118 flags
|= BControlLook::B_DISABLED
;
1120 if (IsFocus() && Window()->IsActive())
1121 flags
|= BControlLook::B_FOCUSED
;
1123 be_control_look
->DrawMenuFieldFrame(this, rect
, updateRect
,
1124 fMenuBar
->LowColor(), LowColor(), flags
);
1129 BMenuField::InitMenu(BMenu
* menu
)
1131 menu
->SetFont(be_plain_font
);
1136 while ((subMenu
= menu
->SubmenuAt(index
++)) != NULL
)
1142 BMenuField::_thread_entry(void* arg
)
1144 return static_cast<BMenuField
*>(arg
)->_MenuTask();
1149 BMenuField::_MenuTask()
1163 tracking
= fMenuBar
->fTracking
;
1178 BMenuField::_UpdateFrame()
1182 if (fLayoutData
->label_layout_item
== NULL
1183 || fLayoutData
->menu_bar_layout_item
== NULL
) {
1187 BRect labelFrame
= fLayoutData
->label_layout_item
->Frame();
1188 BRect menuFrame
= fLayoutData
->menu_bar_layout_item
->Frame();
1190 if (!labelFrame
.IsValid() || !menuFrame
.IsValid())
1194 fDivider
= menuFrame
.left
- labelFrame
.left
;
1197 MoveTo(labelFrame
.left
, labelFrame
.top
);
1198 BSize oldSize
= Bounds().Size();
1199 ResizeTo(menuFrame
.left
+ menuFrame
.Width() - labelFrame
.left
,
1200 menuFrame
.top
+ menuFrame
.Height() - labelFrame
.top
);
1201 BSize newSize
= Bounds().Size();
1203 // If the size changes, ResizeTo() will trigger a relayout, otherwise
1204 // we need to do that explicitly.
1205 if (newSize
!= oldSize
)
1211 BMenuField::_InitMenuBar(BMenu
* menu
, BRect frame
, bool fixedSize
)
1218 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0) {
1219 fMenuBar
= new _BMCMenuBar_(this);
1221 frame
.left
= _MenuBarOffset();
1222 frame
.top
= kVMargin
;
1223 frame
.right
-= kVMargin
;
1224 frame
.bottom
-= kVMargin
;
1226 TRACE("frame(%.1f, %.1f, %.1f, %.1f) (%.2f, %.2f)\n",
1227 frame
.left
, frame
.top
, frame
.right
, frame
.bottom
,
1228 frame
.Width(), frame
.Height());
1230 fMenuBar
= new _BMCMenuBar_(frame
, fixedSize
, this);
1234 // align the menu bar in the full available space
1235 fMenuBar
->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH
,
1236 B_ALIGN_VERTICAL_UNSET
));
1238 // align the menu bar left in the available space
1239 fMenuBar
->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT
,
1240 B_ALIGN_VERTICAL_UNSET
));
1244 fMenuBar
->AddItem(menu
);
1245 fMenuBar
->SetFont(be_plain_font
);
1250 BMenuField::_InitMenuBar(const BMessage
* archive
)
1253 if (archive
->FindBool("be:fixeds", &fixed
) == B_OK
)
1254 fFixedSizeMB
= fixed
;
1256 fMenuBar
= (BMenuBar
*)FindView("_mc_mb_");
1257 if (fMenuBar
== NULL
) {
1258 _InitMenuBar(new BMenu(""), BRect(0, 0, 100, 15), fFixedSizeMB
);
1261 fMenuBar
->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN
));
1262 // this is normally done in InitObject2()
1265 fMenu
= fMenuBar
->SubmenuAt(0);
1268 if (archive
->FindBool("_disable", &disable
) == B_OK
)
1269 SetEnabled(!disable
);
1272 archive
->FindBool("be:dmark", &dmark
);
1273 _BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
);
1274 if (menuBar
!= NULL
)
1275 menuBar
->TogglePopUpMarker(dmark
);
1280 BMenuField::_ValidateLayoutData()
1284 if (fLayoutData
->valid
)
1287 // cache font height
1288 font_height
& fh
= fLayoutData
->font_info
;
1291 const char* label
= Label();
1292 if (label
!= NULL
) {
1293 fLayoutData
->label_width
= ceilf(StringWidth(label
));
1294 fLayoutData
->label_height
= ceilf(fh
.ascent
) + ceilf(fh
.descent
);
1296 fLayoutData
->label_width
= 0;
1297 fLayoutData
->label_height
= 0;
1300 // compute the minimal divider
1302 if (fLayoutData
->label_width
> 0) {
1303 divider
= fLayoutData
->label_width
1304 + be_control_look
->DefaultLabelSpacing();
1307 // If we shan't do real layout, we let the current divider take influence.
1308 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
1309 divider
= std::max(divider
, fDivider
);
1311 // get the minimal (== preferred) menu bar size
1312 // TODO: BMenu::MinSize() is using the ResizeMode() to decide the
1313 // minimum width. If the mode is B_FOLLOW_LEFT_RIGHT, it will use the
1314 // parent's frame width or window's frame width. So at least the returned
1315 // size is wrong, but apparantly it doesn't have much bad effect.
1316 fLayoutData
->menu_bar_min
= fMenuBar
->MinSize();
1318 TRACE("menu bar min width: %.2f\n", fLayoutData
->menu_bar_min
.width
);
1320 // compute our minimal (== preferred) size
1321 BSize
min(fLayoutData
->menu_bar_min
);
1322 min
.width
+= 2 * kVMargin
;
1323 min
.height
+= 2 * kVMargin
;
1326 min
.width
+= divider
;
1328 if (fLayoutData
->label_height
> min
.height
)
1329 min
.height
= fLayoutData
->label_height
;
1331 fLayoutData
->min
= min
;
1333 fLayoutData
->valid
= true;
1334 ResetLayoutInvalidation();
1336 TRACE("width: %.2f, height: %.2f\n", min
.width
, min
.height
);
1341 BMenuField::_MenuBarOffset() const
1343 return std::max(fDivider
+ kVMargin
, kVMargin
);
1348 BMenuField::_MenuBarWidth() const
1350 return Bounds().Width() - (_MenuBarOffset() + kVMargin
);
1355 BMenuField::_DoneTracking(BPoint point
)
1357 Window()->RemoveCommonFilter(fMouseDownFilter
);
1362 BMenuField::_Track(BPoint point
, uint32
)
1367 // #pragma mark - BMenuField::LabelLayoutItem
1370 BMenuField::LabelLayoutItem::LabelLayoutItem(BMenuField
* parent
)
1378 BMenuField::LabelLayoutItem::LabelLayoutItem(BMessage
* from
)
1380 BAbstractLayoutItem(from
),
1384 from
->FindRect(kFrameField
, &fFrame
);
1389 BMenuField::LabelLayoutItem::IsVisible()
1391 return !fParent
->IsHidden(fParent
);
1396 BMenuField::LabelLayoutItem::SetVisible(bool visible
)
1403 BMenuField::LabelLayoutItem::Frame()
1410 BMenuField::LabelLayoutItem::SetFrame(BRect frame
)
1413 fParent
->_UpdateFrame();
1418 BMenuField::LabelLayoutItem::SetParent(BMenuField
* parent
)
1425 BMenuField::LabelLayoutItem::View()
1432 BMenuField::LabelLayoutItem::BaseMinSize()
1434 fParent
->_ValidateLayoutData();
1436 if (fParent
->Label() == NULL
)
1437 return BSize(-1, -1);
1439 return BSize(fParent
->fLayoutData
->label_width
1440 + be_control_look
->DefaultLabelSpacing(),
1441 fParent
->fLayoutData
->label_height
);
1446 BMenuField::LabelLayoutItem::BaseMaxSize()
1448 return BaseMinSize();
1453 BMenuField::LabelLayoutItem::BasePreferredSize()
1455 return BaseMinSize();
1460 BMenuField::LabelLayoutItem::BaseAlignment()
1462 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
1467 BMenuField::LabelLayoutItem::Archive(BMessage
* into
, bool deep
) const
1469 BArchiver
archiver(into
);
1470 status_t err
= BAbstractLayoutItem::Archive(into
, deep
);
1473 err
= into
->AddRect(kFrameField
, fFrame
);
1475 return archiver
.Finish(err
);
1480 BMenuField::LabelLayoutItem::Instantiate(BMessage
* from
)
1482 if (validate_instantiation(from
, "BMenuField::LabelLayoutItem"))
1483 return new LabelLayoutItem(from
);
1489 // #pragma mark - BMenuField::MenuBarLayoutItem
1492 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMenuField
* parent
)
1497 // by default the part right of the divider shall have an unlimited maximum
1499 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
1503 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMessage
* from
)
1505 BAbstractLayoutItem(from
),
1509 from
->FindRect(kFrameField
, &fFrame
);
1514 BMenuField::MenuBarLayoutItem::IsVisible()
1516 return !fParent
->IsHidden(fParent
);
1521 BMenuField::MenuBarLayoutItem::SetVisible(bool visible
)
1528 BMenuField::MenuBarLayoutItem::Frame()
1535 BMenuField::MenuBarLayoutItem::SetFrame(BRect frame
)
1538 fParent
->_UpdateFrame();
1543 BMenuField::MenuBarLayoutItem::SetParent(BMenuField
* parent
)
1550 BMenuField::MenuBarLayoutItem::View()
1557 BMenuField::MenuBarLayoutItem::BaseMinSize()
1559 fParent
->_ValidateLayoutData();
1561 BSize size
= fParent
->fLayoutData
->menu_bar_min
;
1562 size
.width
+= 2 * kVMargin
;
1563 size
.height
+= 2 * kVMargin
;
1570 BMenuField::MenuBarLayoutItem::BaseMaxSize()
1572 BSize
size(BaseMinSize());
1573 size
.width
= B_SIZE_UNLIMITED
;
1580 BMenuField::MenuBarLayoutItem::BasePreferredSize()
1582 return BaseMinSize();
1587 BMenuField::MenuBarLayoutItem::BaseAlignment()
1589 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
1594 BMenuField::MenuBarLayoutItem::Archive(BMessage
* into
, bool deep
) const
1596 BArchiver
archiver(into
);
1597 status_t err
= BAbstractLayoutItem::Archive(into
, deep
);
1600 err
= into
->AddRect(kFrameField
, fFrame
);
1602 return archiver
.Finish(err
);
1607 BMenuField::MenuBarLayoutItem::Instantiate(BMessage
* from
)
1609 if (validate_instantiation(from
, "BMenuField::MenuBarLayoutItem"))
1610 return new MenuBarLayoutItem(from
);
1616 B_IF_GCC_2(InvalidateLayout__10BMenuFieldb
, _ZN10BMenuField16InvalidateLayoutEb
)(
1617 BMenuField
* field
, bool descendants
)
1619 perform_data_layout_invalidated data
;
1620 data
.descendants
= descendants
;
1622 field
->Perform(PERFORM_CODE_LAYOUT_INVALIDATED
, &data
);