Now that child classes aren't overriding the various InvalidateLayout() methods, we can take advantage of some guarantees to simplify/clean/optimize the layout invalidation propagation mechanisms!
This commit is contained in:
@@ -90,8 +90,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
friend class BView;
|
friend class BView;
|
||||||
|
|
||||||
bool InvalidateLayoutsForView(BView* view);
|
|
||||||
bool InvalidationLegal();
|
|
||||||
void SetOwner(BView* owner);
|
void SetOwner(BView* owner);
|
||||||
void SetTarget(BView* target);
|
void SetTarget(BView* target);
|
||||||
|
|
||||||
|
|||||||
@@ -299,8 +299,10 @@ BLayout::InvalidateLayout(bool children)
|
|||||||
// printf("BLayout(%p)::InvalidateLayout(%i) : state %x, disabled %li\n",
|
// printf("BLayout(%p)::InvalidateLayout(%i) : state %x, disabled %li\n",
|
||||||
// this, children, (unsigned int)fState, fInvalidationDisabled);
|
// this, children, (unsigned int)fState, fInvalidationDisabled);
|
||||||
|
|
||||||
if (!InvalidationLegal())
|
if (fInvalidationDisabled > 0
|
||||||
|
|| (fState & B_LAYOUT_INVALIDATION_ILLEGAL) != 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fState |= B_LAYOUT_NECESSARY;
|
fState |= B_LAYOUT_NECESSARY;
|
||||||
LayoutInvalidated(children);
|
LayoutInvalidated(children);
|
||||||
@@ -310,23 +312,15 @@ BLayout::InvalidateLayout(bool children)
|
|||||||
ItemAt(i)->InvalidateLayout(children);
|
ItemAt(i)->InvalidateLayout(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fOwner && BView::Private(fOwner).MinMaxValid())
|
if (fOwner)
|
||||||
fOwner->InvalidateLayout(children);
|
fOwner->InvalidateLayout(children);
|
||||||
|
|
||||||
if (BLayout* nestedIn = Layout()) {
|
if (BLayout* nestedIn = Layout()) {
|
||||||
if (nestedIn->InvalidationLegal())
|
nestedIn->InvalidateLayout();
|
||||||
nestedIn->InvalidateLayout();
|
|
||||||
} else if (fOwner) {
|
} else if (fOwner) {
|
||||||
// If we weren't added as a BLayoutItem, we still have to invalidate
|
// If we weren't added as a BLayoutItem, we still have to invalidate
|
||||||
// whatever layout our owner is in.
|
// whatever layout our owner is in.
|
||||||
BView* ownerParent = fOwner->fParent;
|
fOwner->_InvalidateParentLayout();
|
||||||
if (ownerParent) {
|
|
||||||
BLayout* layout = ownerParent->GetLayout();
|
|
||||||
if (layout && layout->fNestedLayouts.CountItems() > 0)
|
|
||||||
layout->InvalidateLayoutsForView(fOwner);
|
|
||||||
else if (BView::Private(ownerParent).MinMaxValid())
|
|
||||||
ownerParent->InvalidateLayout(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,31 +585,6 @@ BLayout::LayoutContext() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
BLayout::InvalidateLayoutsForView(BView* view)
|
|
||||||
{
|
|
||||||
BView::Private viewPrivate(view);
|
|
||||||
int32 count = viewPrivate.CountLayoutItems();
|
|
||||||
if (count == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (int32 i = 0; i < count; i++) {
|
|
||||||
BLayout* layout = viewPrivate.LayoutItemAt(i)->Layout();
|
|
||||||
if (layout->InvalidationLegal())
|
|
||||||
layout->InvalidateLayout();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
BLayout::InvalidationLegal()
|
|
||||||
{
|
|
||||||
return fInvalidationDisabled <= 0
|
|
||||||
&& (fState & B_LAYOUT_INVALIDATION_ILLEGAL) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BLayout::SetOwner(BView* owner)
|
BLayout::SetOwner(BView* owner)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011, Haiku, Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#include "LayoutInvalidator.h"
|
||||||
|
|
||||||
|
#include <Layout.h>
|
||||||
|
#include <LayoutItem.h>
|
||||||
|
#include <View.h>
|
||||||
|
#include <ViewPrivate.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Handles propagation of layout invalidations between the various classes.
|
||||||
|
* keeps the propagation logic in one place
|
||||||
|
* lets the other classes focus on invalidation, not propagation logic
|
||||||
|
* along with NVI, makes it easier to ensure that we are not
|
||||||
|
invalidating the same objects over and over.
|
||||||
|
|
||||||
|
TODO: could be optimized further if we can be certain that encountering
|
||||||
|
an already invalidated object means we are done.
|
||||||
|
|
||||||
|
Note: we must be certain of this for BLayout objects, if we want to have
|
||||||
|
any reasonable way to invalidate from a set of BLayoutItems without
|
||||||
|
invalidating all of their parent objects n times.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class LayoutInvalidator {
|
||||||
|
public:
|
||||||
|
|
||||||
|
class Layout;
|
||||||
|
class View;
|
||||||
|
class LayoutItem;
|
||||||
|
|
||||||
|
/* Since we use templates, we don't need these in the base class,
|
||||||
|
but these methods are conceptually virtual.
|
||||||
|
virtual bool IsFinished() = 0;
|
||||||
|
virtual *this InvalidateChildren() = 0;
|
||||||
|
virtual *this DoInvalidation() = 0; // actually does the invalidation
|
||||||
|
virtual void Up() = 0; // propagates upwards
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static void InvalidateLayout(T from, bool deep = false)
|
||||||
|
{
|
||||||
|
if (LayoutInvalidator::For(from).IsFinished())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (deep) {
|
||||||
|
LayoutInvalidator::For(from)
|
||||||
|
.DoInvalidation()
|
||||||
|
.InvalidateChildren()
|
||||||
|
.Up();
|
||||||
|
} else {
|
||||||
|
LayoutInvalidator::For(from)
|
||||||
|
.DoInvalidation()
|
||||||
|
.Up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LayoutInvalidator::Layout For(BLayout* layout);
|
||||||
|
static LayoutInvalidator::View For(BView* view);
|
||||||
|
static LayoutInvalidator::LayoutItem For(BLayoutItem* item);
|
||||||
|
|
||||||
|
class View {
|
||||||
|
public:
|
||||||
|
View(BView* view)
|
||||||
|
:
|
||||||
|
fView(view),
|
||||||
|
fViewPrivate(view)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFinished()
|
||||||
|
{
|
||||||
|
return fView != NULL;
|
||||||
|
// TODO: test other conditions
|
||||||
|
}
|
||||||
|
|
||||||
|
View& InvalidateChildren()
|
||||||
|
{
|
||||||
|
// fViewPrivate.InvalidateChildren();
|
||||||
|
// TODO: propagate downwards
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
View& DoInvalidation()
|
||||||
|
{
|
||||||
|
// fViewPrivate.InvalidateLayout();
|
||||||
|
// BLayout::Private(fView->GetLayout()).InvalidateLayout();
|
||||||
|
// We don't invalidate layout items, if a BView subclass requires
|
||||||
|
// that behaviour, it can be implemented there.
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
View& View::Up()
|
||||||
|
{
|
||||||
|
BLayout* layout = fView->GetLayout();
|
||||||
|
if (layout && layout->Layout()) {
|
||||||
|
LayoutInvalidator::InvalidateLayout(layout->Layout());
|
||||||
|
} else if (fViewPrivate.CountLayoutItems() > 0) {
|
||||||
|
_UpThroughLayoutItems();
|
||||||
|
} else {
|
||||||
|
LayoutInvalidator::InvalidateLayout(fView->Parent());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _UpThroughLayoutItems()
|
||||||
|
{
|
||||||
|
int32 count = fViewPrivate.CountLayoutItems();
|
||||||
|
for (int32 i = 0; i < count; i++) {
|
||||||
|
BLayoutItem* item = fViewPrivate.LayoutItemAt(i);
|
||||||
|
LayoutInvalidator::InvalidateLayout(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BView* fView;
|
||||||
|
BView::Private fViewPrivate;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Layout {
|
||||||
|
public:
|
||||||
|
Layout(BLayout* layout)
|
||||||
|
:
|
||||||
|
fLayout(layout)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFinished()
|
||||||
|
{
|
||||||
|
// return !fLayout->LayoutIsValid();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout& InvalidateChildren()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
for (int32 i = fLayout->CountItems() - 1; i >= 0 i--;)
|
||||||
|
BLayoutItem::Private(fLayout->ItemAt(i)).InvalidateLayout(true);
|
||||||
|
*/
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout& DoInvalidation()
|
||||||
|
{
|
||||||
|
BLayout::Private(fLayout).InvalidateLayout();
|
||||||
|
BView::Private(fLayout->Owner()).InvalidateLayout();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Up()
|
||||||
|
{
|
||||||
|
if (fLayout->Layout())
|
||||||
|
LayoutInvalidator::InvalidateLayout(fLayout->Layout());
|
||||||
|
else if (fLayout->Owner())
|
||||||
|
LayoutInvalidator::For(fLayout->Owner()).Up();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BLayout* fLayout;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LayoutItem {
|
||||||
|
public:
|
||||||
|
LayoutItem(BLayoutItem* layoutItem)
|
||||||
|
:
|
||||||
|
fLayoutItem(layoutItem)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFinished()
|
||||||
|
{
|
||||||
|
return !fLayoutItem->LayoutIsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutItem& InvalidateChildren()
|
||||||
|
{
|
||||||
|
/* what to do here?? */
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutItem& DoInvalidation()
|
||||||
|
{
|
||||||
|
BLayoutItem::Private(fLayoutItem).InvalidateLayout();
|
||||||
|
BView::Private(fLayoutItem->View()).InvalidateLayout();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Up()
|
||||||
|
{
|
||||||
|
if (fLayoutItem->Layout())
|
||||||
|
BLayoutInvalidator::_InvalidateLayout(fLayoutItem->Layout());
|
||||||
|
else if (fLayoutItem->View())
|
||||||
|
BLayoutInvalidator::For(fLayoutItem->View()).Up();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BLayoutItem* fLayoutItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline LayoutInvalidator::Layout
|
||||||
|
LayoutInvalidator::For(BLayout* layout)
|
||||||
|
{
|
||||||
|
return BLayoutInvalidator::Layout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LayoutInvalidator::View
|
||||||
|
LayoutInvalidator::For(BView* view)
|
||||||
|
{
|
||||||
|
return BLayoutInvalidator::View(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LayoutInvalidator::LayoutItem
|
||||||
|
LayoutInvalidator::For(BLayoutItem* item)
|
||||||
|
{
|
||||||
|
return LayoutInvalidator::LayoutItem(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BLayoutInvalidator::InvalidateLayout(BLayout* layout, bool deep)
|
||||||
|
{
|
||||||
|
LayoutInvalidator::For(layout).InvalidateLayout(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BLayoutInvalidator::InvalidateLayout(BLayoutItem* item, bool deep)
|
||||||
|
{
|
||||||
|
LayoutInvalidator::For(item).InvalidateLayout(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BLayoutInvalidator::InvalidateLayout(BView* view, bool deep)
|
||||||
|
{
|
||||||
|
LayoutInvalidator::For(view).InvalidateLayout(deep);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011, Haiku, Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _LAYOUT_INVALIDATOR_H
|
||||||
|
#define _LAYOUT_INVALIDATOR_H
|
||||||
|
|
||||||
|
|
||||||
|
class BLayout;
|
||||||
|
class BLayoutItem;
|
||||||
|
class BView;
|
||||||
|
|
||||||
|
|
||||||
|
struct BLayoutInvalidator {
|
||||||
|
static void InvalidateLayout(BLayout* layout, bool deep);
|
||||||
|
static void InvalidateLayout(BLayoutItem* item, bool deep);
|
||||||
|
static void InvalidateLayout(BView* view, bool deep);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
+30
-26
@@ -4692,32 +4692,30 @@ BView::InvalidateLayout(bool descendants)
|
|||||||
// this, descendants, fLayoutData->fLayoutValid,
|
// this, descendants, fLayoutData->fLayoutValid,
|
||||||
// fLayoutData->fLayoutInProgress);
|
// fLayoutData->fLayoutInProgress);
|
||||||
|
|
||||||
if (fLayoutData->fMinMaxValid && !fLayoutData->fLayoutInProgress
|
if (!fLayoutData->fMinMaxValid || fLayoutData->fLayoutInProgress
|
||||||
&& fLayoutData->fLayoutInvalidationDisabled == 0) {
|
|| fLayoutData->fLayoutInvalidationDisabled > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fLayoutData->fLayoutValid = false;
|
fLayoutData->fLayoutValid = false;
|
||||||
fLayoutData->fMinMaxValid = false;
|
fLayoutData->fMinMaxValid = false;
|
||||||
LayoutInvalidated(descendants);
|
LayoutInvalidated(descendants);
|
||||||
|
|
||||||
if (descendants) {
|
if (descendants) {
|
||||||
for (BView* child = fFirstChild;
|
for (BView* child = fFirstChild;
|
||||||
child; child = child->fNextSibling) {
|
child; child = child->fNextSibling) {
|
||||||
child->InvalidateLayout(descendants);
|
child->InvalidateLayout(descendants);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fLayoutData->fLayout && fLayoutData->fLayout->InvalidationLegal())
|
|
||||||
fLayoutData->fLayout->InvalidateLayout(descendants);
|
|
||||||
else if (!fLayoutData->fLayout && fParent) {
|
|
||||||
_InvalidateParentLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fTopLevelView) {
|
|
||||||
// trigger layout process
|
|
||||||
if (fOwner)
|
|
||||||
fOwner->PostMessage(B_LAYOUT_WINDOW);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fLayoutData->fLayout)
|
||||||
|
fLayoutData->fLayout->InvalidateLayout(descendants);
|
||||||
|
else if (fParent) {
|
||||||
|
_InvalidateParentLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fTopLevelView && fOwner)
|
||||||
|
fOwner->PostMessage(B_LAYOUT_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4936,12 +4934,18 @@ BView::_LayoutLeft(BLayout* deleted)
|
|||||||
void
|
void
|
||||||
BView::_InvalidateParentLayout()
|
BView::_InvalidateParentLayout()
|
||||||
{
|
{
|
||||||
|
if (!fParent)
|
||||||
|
return;
|
||||||
|
|
||||||
BLayout* layout = fLayoutData->fLayout;
|
BLayout* layout = fLayoutData->fLayout;
|
||||||
BLayout* layoutParent = layout ? layout->Layout() : NULL;
|
BLayout* layoutParent = layout ? layout->Layout() : NULL;
|
||||||
if (layoutParent && layoutParent->InvalidationLegal()) {
|
if (layoutParent) {
|
||||||
layout->Layout()->InvalidateLayout();
|
layoutParent->InvalidateLayout();
|
||||||
} else if (fParent && fParent->fLayoutData->fLayout) {
|
} else if (fLayoutData->fLayoutItems.CountItems() > 0) {
|
||||||
fParent->fLayoutData->fLayout->InvalidateLayoutsForView(this);
|
int32 count = fLayoutData->fLayoutItems.CountItems();
|
||||||
|
for (int32 i = 0; i < count; i++) {
|
||||||
|
fLayoutData->fLayoutItems.ItemAt(i)->Layout()->InvalidateLayout();
|
||||||
|
}
|
||||||
} else if (fParent) {
|
} else if (fParent) {
|
||||||
fParent->InvalidateLayout();
|
fParent->InvalidateLayout();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user