[haiku-development] Re: Layout problems

  • From: Sean Healy <jalopeura@xxxxxxxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Tue, 31 Mar 2009 19:06:57 +0200

Stephan Assmus wrote:

From your pictures and description, I don't understand which views exactly
are the problem. Could you post your code? You can also send it to me in private, if you prefer.

Basically the problem is that the view on top contains a single line of text and is expanding to fill all the available area, so everything is shoved to the bottom of the containing view.

I was hoping someone would have had a similar problem in the past and know how to point me in the right direction. I didn't want to post code because there's a lot of it.



Adding a GroupLayout to an existing container control:

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);


The child controls are created on the fly, so in a loop I add them to the layout, but first I make the background red so I can see which controls are taking up all the space:

parameterView->SetViewColor(255,0,0);

<snip>

//view->AddChild(parameterView);
viewLayout->AddView(parameterView);


After the loop, I tried adding some glue, but it didn't help.

viewLayout->AddItem(BSpaceLayoutItem::CreateGlue());


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;

        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);

        // Create the parameter views - but don't add them yet

        rect.OffsetTo(B_ORIGIN);
        rect.InsetBySelf(5, 5);

        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;
        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));
                }

                //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;
        }

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

        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);
        }

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

        // 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());
                }
        }

        viewLayout->AddItem(BSpaceLayoutItem::CreateGlue());
        view->SetContentBounds(view->Bounds());
        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)
{
        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;
        }
}






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;
}




SeparatorView::SeparatorView(BRect frame)
        : BView(frame, "-", B_FOLLOW_NONE, B_WILL_DRAW)
{
        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));
}


Other related posts: