I'm having a problem that I believe is connected to the layout system. I have a GroupView within a ScrollView; if there is more than one group, each ScrollView gets placed into a TabView.
If there's only one group (and therefore no TabView), everything works fine. The scrollbar is the right size and I can scroll all the way to the bottom of the contained GroupView.
However, when there's a TabView involved, the ScrollView is taller than the TabView's container, and I can't figure out why. The scrollbar is one long button, and so I can't scroll to the stuff that doesn't fit in the TabView's dimensions.
Here is some output. Notice that even though the container is resized to dimensions that look okay, when the event gets to the ScrollView's FrameResized() function, the height is several times too large.
TabView ViewForTab resized to -000000006x-000000027 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: -000000006x-000000028 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: 0000000000x0000000000 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: 0000000000x0000000000 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: 0000000000x0000000000 DynamicScrollView frame resized in FrameResized to 0000000435x0000006104 DynamicScrollView: Updating bars Target: 0000000435x0000003044 Scroller: 0000000435x0000006104 DynamicScrollView frame resized in FrameResized to 0000000435x0000006104 DynamicScrollView: Updating bars Target: 0000000420x0000006104 Scroller: 0000000435x0000006104 TabView container resized to 0000000435x0000000584 DynamicScrollView frame resized in FrameResized to 0000000435x0000006104 DynamicScrollView: Updating bars Target: 0000000420x0000006104 Scroller: 0000000435x0000006104 DynamicScrollView frame resized in FrameResized to 0000000435x0000006163 DynamicScrollView: Updating bars Target: 0000000435x0000003081 Scroller: 0000000435x0000006163 TabView ViewForTab resized to -000000006x-000000027 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: -000000006x-000000028 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: 0000000000x0000000000 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: 0000000000x0000000000 DynamicScrollView: Updating bars Target: 0000000000x0000000000 Scroller: 0000000000x0000000000 DynamicScrollView frame resized in FrameResized to 0000000435x0000006104 DynamicScrollView: Updating bars Target: 0000000435x0000003044 Scroller: 0000000435x0000006104 DynamicScrollView frame resized in FrameResized to 0000000435x0000006104 DynamicScrollView: Updating bars Target: 0000000420x0000006104 Scroller: 0000000435x0000006104 TabView container resized to 0000000435x0000000584 DynamicScrollView frame resized in FrameResized to 0000000435x0000006104 DynamicScrollView: Updating bars Target: 0000000420x0000006104 Scroller: 0000000435x0000006104 DynamicScrollView frame resized in FrameResized to 0000000435x0000006163 DynamicScrollView: Updating bars Target: 0000000435x0000003081 Scroller: 0000000435x0000006163 Here is the code for the three classes involved. // #pragma mark - DynamicScrollView::DynamicScrollView(const char *name, BView *target)//: BView(target->Frame(), name, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS),
: BGroupView(B_VERTICAL, 0), fHorizontalScrollBar(NULL), fVerticalScrollBar(NULL), fTarget(target), fIsDocumentScroller(false) { SetName(name), SetFlags(B_WILL_DRAW | B_FRAME_EVENTS), //fContentBounds.Set(-1, -1, -1, -1); //SetViewColor(fTarget->ViewColor()); SetViewColor(255,0,0); //target->MoveTo(B_ORIGIN); GetLayout()->AddView(target); } DynamicScrollView::~DynamicScrollView() { } void DynamicScrollView::AttachedToWindow(void) { BRect frame = ConvertToScreen(Bounds()); BRect windowFrame = Window()->Frame(); fIsDocumentScroller = Parent() == NULL && Window() != NULL && Window()->Look() == B_DOCUMENT_WINDOW_LOOK && frame.right == windowFrame.right && frame.bottom == windowFrame.bottom; UpdateBars(); } void DynamicScrollView::FrameResized(float width, float height) { ERROR("DynamicScrollView frame resized in FrameResized to %010dx%010d\n", int(width), int(height) ); UpdateBars(); } void DynamicScrollView::FrameMoved(BPoint newPosition) { UpdateBars(); } void DynamicScrollView::GetPreferredSize(float *_width, float *_height) { float width = 50; if (fVerticalScrollBar) width += B_V_SCROLL_BAR_WIDTH; float height = 50; if (fHorizontalScrollBar) height += B_H_SCROLL_BAR_HEIGHT; if (_width) *_width = width; if (_height) *_height = height; } void DynamicScrollView::SetContentBounds(BRect bounds) { fContentBounds = bounds; if (Window()) UpdateBars(); } void DynamicScrollView::UpdateBars() { // we need the size that the view wants to have, and the one // it could have (without the space for the scroll bars) float width, height; //if (fContentBounds == BRect(-1, -1, -1, -1)) fTarget->GetPreferredSize(&width, &height); //else { // width = fContentBounds.Width(); // height = fContentBounds.Height(); //} BRect bounds = Bounds();ERROR("DynamicScrollView: Updating bars\n\tTarget: %010dx%010d\n\tScroller: %010dx%010d\n",
int(width), int(height), int(bounds.Width()), int(bounds.Height()) ); // do we have to remove a scroll bar? bool horizontal = width > bounds.Width(); bool vertical = height > bounds.Height(); // for testing vertical = true; if (!horizontal && fHorizontalScrollBar != NULL) { RemoveChild(fHorizontalScrollBar); delete fHorizontalScrollBar; fHorizontalScrollBar = NULL; } if (!vertical && fVerticalScrollBar != NULL) { RemoveChild(fVerticalScrollBar); delete fVerticalScrollBar; fVerticalScrollBar = NULL; } // or do we have to add a scroll bar? if (horizontal && fHorizontalScrollBar == NULL) { BRect rect = Bounds(); rect.top = rect.bottom + 1 - B_H_SCROLL_BAR_HEIGHT; if (vertical || fIsDocumentScroller) rect.right -= B_V_SCROLL_BAR_WIDTH; fHorizontalScrollBar = new BScrollBar(rect, "horizontal", fTarget, 0, width, B_HORIZONTAL); AddChild(fHorizontalScrollBar); } if (vertical && fVerticalScrollBar == NULL) { BRect rect = Bounds(); rect.left = rect.right + 1 - B_V_SCROLL_BAR_WIDTH; if (horizontal || fIsDocumentScroller) rect.bottom -= B_H_SCROLL_BAR_HEIGHT; fVerticalScrollBar = new BScrollBar(rect, "vertical", fTarget, 0, height, B_VERTICAL); AddChild(fVerticalScrollBar); } // update the scroll bar range & proportions and layout views bounds = Bounds(); if (fHorizontalScrollBar != NULL) bounds.bottom -= B_H_SCROLL_BAR_HEIGHT + 1; if (fVerticalScrollBar != NULL) bounds.right -= B_V_SCROLL_BAR_WIDTH + 1; fTarget->MoveTo(bounds.LeftTop()); fTarget->ResizeTo(bounds.Width(), bounds.Height()); if (fHorizontalScrollBar != NULL) { float delta = width - bounds.Width(); if (delta < 0) delta = 0; fHorizontalScrollBar->SetRange(0, delta); fHorizontalScrollBar->SetSteps(1, bounds.Width()); fHorizontalScrollBar->SetProportion(bounds.Width() / width); float barWidth = Bounds().Width(); if (vertical) { // scrollbars overlap one pixel of the frame barWidth += 1; } if (vertical || fIsDocumentScroller) barWidth -= B_V_SCROLL_BAR_WIDTH + 1; fHorizontalScrollBar->MoveTo(bounds.left, bounds.bottom + 1); fHorizontalScrollBar->ResizeTo(barWidth, B_H_SCROLL_BAR_HEIGHT); } if (fVerticalScrollBar != NULL) { float delta = height - bounds.Height(); if (delta < 0) delta = 0; fVerticalScrollBar->SetRange(0, delta); fVerticalScrollBar->SetSteps(1, bounds.Height()); fVerticalScrollBar->SetProportion(bounds.Height() / height); float barHeight = Bounds().Height(); if (horizontal) { // scrollbars overlap one pixel of the frame barHeight += 1; } if (horizontal || fIsDocumentScroller) barHeight -= B_H_SCROLL_BAR_HEIGHT + 1; fVerticalScrollBar->MoveTo(bounds.right + 1, bounds.top); fVerticalScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, barHeight); } } // #pragma mark -GroupView::GroupView(const char *name, enum orientation orientation, float spacing)
: BGroupView(orientation, spacing) { SetName(name); SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); SetViewColor(0,255,0); } GroupView::~GroupView() { } // #pragma mark - /** BTabView is really stupid - it doesn't even resize its content * view when it is resized itself. * This derived class fixes this issue, and also resizes all tab * content views to the size of the container view when they are * selected (does not take their resize flags into account, though). */ //TabView::TabView(BRect frame, const char *name, button_width width, // uint32 resizingMode, uint32 flags) // : BTabView(frame, name, width, resizingMode, flags) TabView::TabView(const char *name, button_width width, uint32 flags) : BTabView(name, width, flags) { } void TabView::FrameResized(float width, float height) { BRect rect = Bounds(); rect.top += TabHeight(); rect.InsetBy(3.0f, 3.0f); //ContainerView is inseted by 3.0 in BTabView::_InitObject() ERROR("TabView container resized to %010dx%010d\n", int(rect.Width()), int(rect.Height()) ); ContainerView()->ResizeTo(rect.Width(), rect.Height()); } void TabView::Select(int32 tab) { BTabView::Select(tab); BView *view = ViewForTab(Selection()); if (view != NULL) { //BRect rect = ContainerView()->Bounds(); BRect rect = Bounds(); rect.top += TabHeight(); rect.InsetBy(3.0f, 3.0f); //ContainerView is inseted by 3.0 in BTabView::_InitObject() ERROR("TabView ViewForTab resized to %010dx%010d\n", int(rect.Width()), int(rect.Height()) ); view->ResizeTo(rect.Width(), rect.Height()); } }