[haiku-development] Re: Layout problems

  • From: Stephan Assmus <superstippi@xxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Tue, 31 Mar 2009 19:40:50 +0200

On 2009-03-31 at 19:20:39 [+0200], Sean Healy <jalopeura@xxxxxxxxxxx> wrote:
> Here's that same code in the context of the function. I can't see that 
> any controls are being added outside of the layout system.
> 
> BView *
> DefaultMediaTheme::MakeViewFor(BParameterGroup& group, const BRect* 
> hintRect)
> {
>     CALLED();
> 
>     if (group.Flags() & B_HIDDEN_PARAMETER)
>         return NULL;
> 
>     BRect rect;
>     if (hintRect != NULL)
>         rect = *hintRect;

You should probably ignore the hintRect now, when changing everything to 
use layouting.


>     GroupView *view = new GroupView(rect, group.Name());
>     
>     BGroupLayout* viewLayout = new BGroupLayout(B_VERTICAL, 5); 
>     viewLayout->SetInsets(5,5,5,5);
>     viewLayout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(5)); 
>     view->SetLayout(viewLayout);

I don't know what else GroupView does, but if it does nothing special, just 
replace this with BGroupView and get the layout via 
BGroupView::GroupLayout();

>     // Create the parameter views - but don't add them yet
> 
>     rect.OffsetTo(B_ORIGIN);
>     rect.InsetBySelf(5, 5);

... no more BRect stuff. :-)

>     BList views;
>     for (int32 i = 0; i < group.CountParameters(); i++) {
>         BParameter *parameter = group.ParameterAt(i);
>         if (parameter == NULL)
>             continue;
> 
>         BView *parameterView = MakeSelfHostingViewFor(*parameter,
>             hintRect ? &rect : NULL);
>         if (parameterView == NULL)
>             continue;
>         parameterView->SetViewColor(view->ViewColor());
>         parameterView->SetViewColor(255,0,0);
>             // ToDo: dunno why this is needed, but the controls // 
>             sometimes (!) have a white background without it
> 
>         views.AddItem(parameterView);
>     }
> 
>     // Identify a title view, and add it at the top if present
> 
>     TitleView *titleView = dynamic_cast<TitleView *>((BView 
>     *)views.ItemAt(0)); if (titleView != NULL) {
>         //view->AddChild(titleView);
>         viewLayout->AddView(titleView);
>         rect.OffsetBy(0, titleView->Bounds().Height());
>     }
> 
>     // Add the sub-group views
> 
>     rect.right = rect.left + 20;
>     rect.bottom = rect.top + 20;

Again, no more messing with rects...

>     float lastHeight = 0;
> 
>     for (int32 i = 0; i < group.CountGroups(); i++) {
>         BParameterGroup *subGroup = group.GroupAt(i);
>         if (subGroup == NULL)
>             continue;
> 
>         BView *groupView = MakeViewFor(*subGroup, &rect);
>         if (groupView == NULL)
>             continue;
> 
>         if (i > 0) {
>             // add separator view
>             BRect separatorRect(groupView->Frame());
>             separatorRect.left -= 3;
>             separatorRect.right = separatorRect.left + 1;
>             if (lastHeight > separatorRect.Height())
>                 separatorRect.bottom = separatorRect.top + lastHeight;
> 
>             //view->AddChild(new SeparatorView(separatorRect)); 
>             viewLayout->AddView(new SeparatorView(separatorRect));

You probably need to change SeparatorView to give it a layout-friendly 
constructor - ie, taking no BRect and using the no BRect-version of the 
BView constructor. This will change the resizing mode of the view to 
B_FOLLOW_NONE and the flags will include B_SUPPORTS_LAYOUT.


>         }
> 
>         //view->AddChild(groupView);
>         viewLayout->AddView(groupView);
> 
>         rect.OffsetBy(groupView->Bounds().Width() + 5, 0);
> 
>         lastHeight = groupView->Bounds().Height();
>         if (lastHeight > rect.Height())
>             rect.bottom = rect.top + lastHeight - 1;

Skip all the BRect calculations.

>     }
> 
>     view->ResizeTo(rect.left + 10, rect.bottom + 5);
>     view->SetContentBounds(view->Bounds());

This should not be needed anymore.

>     if (group.CountParameters() == 0)
>         return view;
> 
>     // add the parameter views part of the group
> 
>     if (group.CountGroups() > 0) {
>         rect.top = rect.bottom + 10;
>         rect.bottom = rect.top + 20;
>     }
> 
>     bool center = false;
> 
>     for (int32 i = 0; i < views.CountItems(); i++) {
>         BView *parameterView = static_cast<BView *>(views.ItemAt(i));
> 
>         if (parameterView->Bounds().Width() + 5 > rect.Width())
>             rect.right = parameterView->Bounds().Width() + rect.left + 5;
> 
>         // we don't need to add the title view again
>         if (parameterView == titleView)
>             continue;
> 
>         // if there is a BChannelSlider (ToDo: or any vertical slider?) 
>         // the views will be centered
>         if (dynamic_cast<BChannelSlider *>(parameterView) != NULL)
>             center = true;
> 
>         parameterView->MoveTo(parameterView->Frame().left, rect.top); 
>         //view->AddChild(parameterView);
>         viewLayout->AddView(parameterView);
> 
>         rect.OffsetBy(0, parameterView->Bounds().Height() + 5);

Again, remove all the rect calculations. BChannelSlider will need to be 
adopted to support layouting. This was somewhere on my TODO list, I could 
help you with that, if you want me to. :-)

>     }
> 
>     if (views.CountItems() > (titleView != NULL ? 1 : 0))
>         view->ResizeTo(rect.right + 5, rect.top + 5);

Again, should be removed.

>     // center the parameter views if needed, and tweak some views
> 
>     float width = view->Bounds().Width();
> 
>     for (int32 i = 0; i < views.CountItems(); i++) {
>         BView *subView = static_cast<BView *>(views.ItemAt(i)); BRect 
>         frame = subView->Frame();
> 
>         if (center)
>             subView->MoveTo((width - frame.Width()) / 2, frame.top);
>         else {
>             // tweak the PopUp views to look better
>             if (dynamic_cast<BOptionPopUp *>(subView) != NULL)
>                 subView->ResizeTo(width, frame.Height());
>         }
>     }

Remove all the above as well.

>     viewLayout->AddItem(BSpaceLayoutItem::CreateGlue()); 
>     view->SetContentBounds(view->Bounds());

Content bounds will be automatically calculated, just remove that from 
GroupView, if you still need to use it at all.

>     return view;
> }
> 
> 
> The external functions that create the views return instances of 
> MenuFields, CheckBoxes, and StringControls, not custom controls. However, 
> here are the custom controls used in the function above:
> 
> TitleView::TitleView(BRect frame, const char *title)
> //    : BView(frame, title, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW)
> //    : BView(frame, title, 0, B_WILL_DRAW)
>     : BView(frame, title, 0, B_WILL_DRAW)

Yep, do not use the BRect BView constructor. Just name and flags:

 : BView(title, B_WILL_DRAW)


> {
>     fTitle = strdup(title);
>     SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
>     SetLowColor(ViewColor());
> }
> 
> 
> TitleView::~TitleView()
> {
>     free((char *)fTitle);
> }
> 
> 
> void
> TitleView::Draw(BRect updateRect)
> {
>     BRect rect(Bounds());
> 
>     SetDrawingMode(B_OP_COPY);
>     SetHighColor(240, 240, 240);
>     DrawString(fTitle, BPoint(rect.left + 1, rect.bottom - 8));
> 
>     SetDrawingMode(B_OP_OVER);
>     SetHighColor(80, 20, 20);
>     DrawString(fTitle, BPoint(rect.left, rect.bottom - 9));
> }
> 
> 
> void
> TitleView::GetPreferredSize(float *_width, float *_height) {
>     if (_width)
>         *_width = StringWidth(fTitle) + 2;
> 
>     if (_height) {
>         font_height fontHeight;
>         GetFontHeight(&fontHeight);
> 
>         *_height = fontHeight.ascent + fontHeight.descent + 
>         fontHeight.leading
>             + 8;
>     }
> }

You can probably just keep this, or you can replace that with MinSize(), 
MaxSize() and PreferredSize() implementations. But I believe the BView 
implementations fall back to using GetPreferredSize() anyways.



> 
> 
> 
> 
> 
> 
> GroupView::GroupView(BRect frame, const char *name)
>     : BView(frame, name, B_FOLLOW_NONE, B_WILL_DRAW)
> {
>     SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
> }
> 
> 
> GroupView::~GroupView()
> {
> }
> 
> 
> void
> GroupView::AttachedToWindow()
> {
>     for (int32 i = CountChildren(); i-- > 0;) {
>         BControl *control = dynamic_cast<BControl *>(ChildAt(i)); if 
>         (control == NULL)
>             continue;
> 
>         control->SetTarget(control);
>     }
> }
> 
> 
> void
> GroupView::AllAttached()
> {
> }
> 
> 
> void
> GroupView::GetPreferredSize(float *_width, float *_height) {
>     if (_width)
>         *_width = fContentBounds.Width();
> 
>     if (_height)
>         *_height = fContentBounds.Height();
> }
> 
> 
> BSize
> GroupView::MinSize()
> {
>     return BSize(100, 100);
> }
> 
> 
> BSize
> GroupView::PreferredSize()
> {
>     return MinSize();
> }
> 
> 
> BSize
> GroupView::MaxSize()
> {
>     BSize max;
>     GetPreferredSize(&max.width, &max.height);
>     return max;
> }
> 
> 
> void
> GroupView::SetContentBounds(BRect bounds)
> {
>     fContentBounds = bounds;
> }

Ah yes. So it looks like you can simply remove GroupView and use BGroupView 
instead.



> SeparatorView::SeparatorView(BRect frame)
>     : BView(frame, "-", B_FOLLOW_NONE, B_WILL_DRAW)

Use the layout-friendly constructor here again.

> {
>     fVertical = frame.Width() < frame.Height();
>     SetViewColor(B_TRANSPARENT_COLOR);
> }
> 
> 
> SeparatorView::~SeparatorView()
> {
> }
> 
> 
> void
> SeparatorView::Draw(BRect updateRect)
> {
>     rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR); BRect rect = 
>     updateRect & Bounds();
> 
>     SetHighColor(tint_color(color, B_DARKEN_1_TINT));
>     if (fVertical)
>         StrokeLine(BPoint(0, rect.top), BPoint(0, rect.bottom));
>     else
>         StrokeLine(BPoint(rect.left, 0), BPoint(rect.right, 0));
> 
>     SetHighColor(tint_color(color, B_LIGHTEN_1_TINT)); if (fVertical)
>         StrokeLine(BPoint(1, rect.top), BPoint(1, rect.bottom));
>     else
>         StrokeLine(BPoint(rect.left, 1), BPoint(rect.right, 1));
> }

And you probably need to implement MinSize, PreferredSize() and MaxSize() 
again.

Best regards,
-Stephan


Other related posts: