[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: