From e7b0dc78f7695340de8d5fd6ab4d0798809e0f0b Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Sat, 22 Oct 2011 19:33:33 -0600 Subject: [PATCH] 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! --- headers/os/interface/Layout.h | 2 - src/kits/interface/Layout.cpp | 43 +--- src/kits/interface/LayoutInvalidator.cpp | 250 +++++++++++++++++++++++ src/kits/interface/LayoutInvalidator.h | 20 ++ src/kits/interface/View.cpp | 56 ++--- 5 files changed, 306 insertions(+), 65 deletions(-) create mode 100644 src/kits/interface/LayoutInvalidator.cpp create mode 100644 src/kits/interface/LayoutInvalidator.h diff --git a/headers/os/interface/Layout.h b/headers/os/interface/Layout.h index c5ec46a613..4e18633671 100644 --- a/headers/os/interface/Layout.h +++ b/headers/os/interface/Layout.h @@ -90,8 +90,6 @@ protected: private: friend class BView; - bool InvalidateLayoutsForView(BView* view); - bool InvalidationLegal(); void SetOwner(BView* owner); void SetTarget(BView* target); diff --git a/src/kits/interface/Layout.cpp b/src/kits/interface/Layout.cpp index 2ccc3020aa..b2176a0dc9 100644 --- a/src/kits/interface/Layout.cpp +++ b/src/kits/interface/Layout.cpp @@ -299,8 +299,10 @@ BLayout::InvalidateLayout(bool children) // printf("BLayout(%p)::InvalidateLayout(%i) : state %x, disabled %li\n", // this, children, (unsigned int)fState, fInvalidationDisabled); - if (!InvalidationLegal()) + if (fInvalidationDisabled > 0 + || (fState & B_LAYOUT_INVALIDATION_ILLEGAL) != 0) { return; + } fState |= B_LAYOUT_NECESSARY; LayoutInvalidated(children); @@ -310,23 +312,15 @@ BLayout::InvalidateLayout(bool children) ItemAt(i)->InvalidateLayout(children); } - if (fOwner && BView::Private(fOwner).MinMaxValid()) + if (fOwner) fOwner->InvalidateLayout(children); if (BLayout* nestedIn = Layout()) { - if (nestedIn->InvalidationLegal()) - nestedIn->InvalidateLayout(); + nestedIn->InvalidateLayout(); } else if (fOwner) { // If we weren't added as a BLayoutItem, we still have to invalidate // whatever layout our owner is in. - BView* ownerParent = fOwner->fParent; - if (ownerParent) { - BLayout* layout = ownerParent->GetLayout(); - if (layout && layout->fNestedLayouts.CountItems() > 0) - layout->InvalidateLayoutsForView(fOwner); - else if (BView::Private(ownerParent).MinMaxValid()) - ownerParent->InvalidateLayout(false); - } + fOwner->_InvalidateParentLayout(); } } @@ -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 BLayout::SetOwner(BView* owner) { diff --git a/src/kits/interface/LayoutInvalidator.cpp b/src/kits/interface/LayoutInvalidator.cpp new file mode 100644 index 0000000000..cbc497f601 --- /dev/null +++ b/src/kits/interface/LayoutInvalidator.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#include "LayoutInvalidator.h" + +#include +#include +#include +#include + + +/* + 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 + 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); +} + diff --git a/src/kits/interface/LayoutInvalidator.h b/src/kits/interface/LayoutInvalidator.h new file mode 100644 index 0000000000..0fc0f2823b --- /dev/null +++ b/src/kits/interface/LayoutInvalidator.h @@ -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 diff --git a/src/kits/interface/View.cpp b/src/kits/interface/View.cpp index fdd305cc66..09fa6e9972 100644 --- a/src/kits/interface/View.cpp +++ b/src/kits/interface/View.cpp @@ -4692,32 +4692,30 @@ BView::InvalidateLayout(bool descendants) // this, descendants, fLayoutData->fLayoutValid, // fLayoutData->fLayoutInProgress); - if (fLayoutData->fMinMaxValid && !fLayoutData->fLayoutInProgress - && fLayoutData->fLayoutInvalidationDisabled == 0) { + if (!fLayoutData->fMinMaxValid || fLayoutData->fLayoutInProgress + || fLayoutData->fLayoutInvalidationDisabled > 0) { + return; + } - fLayoutData->fLayoutValid = false; - fLayoutData->fMinMaxValid = false; - LayoutInvalidated(descendants); + fLayoutData->fLayoutValid = false; + fLayoutData->fMinMaxValid = false; + LayoutInvalidated(descendants); - if (descendants) { - for (BView* child = fFirstChild; - child; child = child->fNextSibling) { - 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 (descendants) { + for (BView* child = fFirstChild; + child; child = child->fNextSibling) { + child->InvalidateLayout(descendants); } } + + 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 BView::_InvalidateParentLayout() { + if (!fParent) + return; + BLayout* layout = fLayoutData->fLayout; BLayout* layoutParent = layout ? layout->Layout() : NULL; - if (layoutParent && layoutParent->InvalidationLegal()) { - layout->Layout()->InvalidateLayout(); - } else if (fParent && fParent->fLayoutData->fLayout) { - fParent->fLayoutData->fLayout->InvalidateLayoutsForView(this); + if (layoutParent) { + layoutParent->InvalidateLayout(); + } else if (fLayoutData->fLayoutItems.CountItems() > 0) { + int32 count = fLayoutData->fLayoutItems.CountItems(); + for (int32 i = 0; i < count; i++) { + fLayoutData->fLayoutItems.ItemAt(i)->Layout()->InvalidateLayout(); + } } else if (fParent) { fParent->InvalidateLayout(); }