xmake.repo/packages/n/noesis/latest/Include/NsGui/ItemContainerGenerator.h
2024-12-22 19:15:02 +08:00

268 lines
9.5 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////////////////
// NoesisGUI - http://www.noesisengine.com
// Copyright (c) Noesis Technologies S.L. All Rights Reserved.
////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef __GUI_ITEMCONTAINERGENERATOR_H__
#define __GUI_ITEMCONTAINERGENERATOR_H__
#include <NsCore/Noesis.h>
#include <NsCore/Delegate.h>
#include <NsCore/BaseComponent.h>
#include <NsGui/CoreApi.h>
#include <NsGui/IRecyclingItemContainerGenerator.h>
#include <NsGui/Events.h>
namespace Noesis
{
class DependencyObject;
class DependencyProperty;
class ItemsControl;
class Panel;
class ItemContainerGeneratorTest;
struct NotifyCollectionChangedEventArgs;
enum NotifyCollectionChangedAction: int32_t;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// The ItemsChanged event is raised by an ItemContainerGenerator to inform layouts that the items
/// collection has changed.
////////////////////////////////////////////////////////////////////////////////////////////////////
struct ItemsChangedEventArgs: public EventArgs
{
NotifyCollectionChangedAction action;
GeneratorPosition position;
GeneratorPosition oldPosition;
int itemCount;
int itemUICount;
ItemsChangedEventArgs(NotifyCollectionChangedAction action, const GeneratorPosition& position,
const GeneratorPosition& oldPosition, int itemCount, int itemUICount);
ItemsChangedEventArgs(NotifyCollectionChangedAction action, const GeneratorPosition& position,
int itemCount, int itemUICount);
};
typedef Delegate<void(BaseComponent*, const ItemsChangedEventArgs&)> ItemsChangedEventHandler;
NS_WARNING_PUSH
NS_MSVC_WARNING_DISABLE(4251 4275)
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Generates the user interface on behalf of its host, such as an ItemsControl.
///
/// http://msdn.microsoft.com/en-us/library/system.windows.controls.itemcontainergenerator.aspx
////////////////////////////////////////////////////////////////////////////////////////////////////
class NS_GUI_CORE_API ItemContainerGenerator: public BaseComponent,
public IRecyclingItemContainerGenerator
{
public:
// TODO: Expose Items as a ReadOnlyCollection<object>
/// Gets the status of the generator
GeneratorStatus GetStatus() const;
/// Raised to inform controls that ItemContainerGenerator status has changed
DelegateEvent_<EventHandler> StatusChanged();
/// Raised by a ItemContainerGenerator to inform layouts that the items collection has changed
DelegateEvent_<ItemsChangedEventHandler> ItemsChanged();
/// Returns the container corresponding to the item at the given index within the Items
DependencyObject* ContainerFromIndex(int index) const;
/// Returns the container corresponding to the given item. Returns null if the item does not
/// belong to the item collection, or if no container has been generated for it
DependencyObject* ContainerFromItem(BaseComponent* item) const;
/// Given a generated container, returns the index of the corresponding item within the Items
int IndexFromContainer(DependencyObject* container) const;
/// Returns the item corresponding to the given container. If the element was not generated as
/// a container for this generator's host, the method returns DependencyProperty.UnsetValue
BaseComponent* ItemFromContainer(DependencyObject* container) const;
/// Starts a container generation batch
void StartBatch();
/// Stops current container generation batch
void StopBatch();
/// From IItemContainerGenerator
//@{
ItemContainerGenerator* GetItemContainerGeneratorForPanel(Panel* panel) const override;
GeneratorPosition GeneratorPositionFromIndex(int itemIndex) const override;
int IndexFromGeneratorPosition(const GeneratorPosition& position) const override;
void StartAt(const GeneratorPosition& position, GeneratorDirection direction) override;
void StartAt(const GeneratorPosition& position, GeneratorDirection direction,
bool allowStartAtRealizedItem) override;
Ptr<DependencyObject> GenerateNext() override;
Ptr<DependencyObject> GenerateNext(bool& isNewlyRealized) override;
void Stop() override;
void PrepareItemContainer(DependencyObject* container) const override;
void Remove(const GeneratorPosition& position, uint32_t count) override;
void RemoveAll() override;
//@}
/// From IRecyclingItemContainerGenerator
//@{
void Recycle(const GeneratorPosition& position, uint32_t count) override;
//@}
NS_IMPLEMENT_INTERFACE_FIXUP
private:
friend class ItemsControl;
friend class VirtualizingStackPanelTestHelper;
ItemContainerGenerator();
ItemContainerGenerator(ItemsControl* host);
void Refresh();
void ResetLastNodeIndex() const;
void SetStatus(GeneratorStatus status);
Ptr<DependencyObject> GenerateNext(bool stopAtRealized, bool& isNewlyRealized);
static BaseComponent* GetContainerItem(DependencyObject* container);
static void SetContainerItem(DependencyObject* container, BaseComponent* item);
void OnItemsChanged(BaseComponent* sender, const NotifyCollectionChangedEventArgs& e);
void OnItemAdded(BaseComponent* item, int index);
void OnItemRemoved(BaseComponent* item, int index);
void OnItemReplaced(BaseComponent* oldItem, BaseComponent* newItem, int index);
void OnItemMoved(BaseComponent* item, int oldIndex, int newIndex);
void OnReset();
void AttachContainerItem(DependencyObject* container, BaseComponent* item);
void DetachContainerItem(DependencyObject* container, BaseComponent* item,
bool setDisconnected);
void UpdateAlternationCount(int oldValue, int newValue);
private:
friend class ItemContainerGeneratorHelperTest;
friend class ItemContainerGeneratorTest;
// Host for generator items
ItemsControl* mHost;
// Stores info about one (realized) or several (unrealized) items
struct ItemNode
{
enum Type { Unrealized, Realized };
Type type;
union
{
struct
{
uint32_t numItems;
}
unrealized;
struct
{
BaseComponent* item;
DependencyObject* container;
}
realized;
};
ItemNode(uint32_t n = 0);
ItemNode(BaseComponent* i, DependencyObject* c);
ItemNode(const ItemNode& node);
ItemNode(ItemNode&& node);
~ItemNode();
ItemNode& operator=(const ItemNode& node);
ItemNode& operator=(ItemNode&& node);
uint32_t NumItems() const;
uint32_t NumContainers() const;
BaseComponent* Item() const;
DependencyObject* Container() const;
bool MoveAllowed(bool allowMove) const;
bool IsRealized() const;
void Unrealize(uint32_t n);
void Realize(BaseComponent* i, DependencyObject* c);
private:
void CopyFrom(const ItemNode& node);
void MoveFrom(ItemNode& node);
void UnrealizeInternal(uint32_t n);
void RealizeInternal(BaseComponent* i, DependencyObject* c);
void Release();
};
// Holds correspondence between Items and their Containers. Usually a group of unrealized
// (virtualized) items, followed by some realized (visible) items, and a final group of
// unrealized items:
//
// ------------ -------------- -------------- ------------
// | n | | 1 item* | | 1 item* | | n |
// | unrealized | -> | 1 container* | -> ... -> | 1 container* | -> | unrealized |
// ------------ -------------- -------------- ------------
//
typedef Vector<ItemNode> ItemNodes;
ItemNodes mItemNodes;
uint32_t mNumItems;
void Remove(const GeneratorPosition& position, uint32_t count, bool recycleContainers,
const char* actionName);
void Remove(uint32_t startIt, int startIndex, uint32_t count, bool allowUnrealizedItems,
bool recycleContainers, const char* actionName);
int RemoveItem(uint32_t it);
void CoalesceNodes(uint32_t prevIt, uint32_t nextIt);
uint32_t FindNodeFromIndex(int index, int& itemIndex, int& offset);
void MoveForward();
void MoveForward(int offset, bool allowStartAtRealizedItem);
void MoveBackward();
void MoveBackward(int offset, bool allowStartAtRealizedItem);
// Stores recycled containers to be used in future generations
typedef Vector<Ptr<DependencyObject>> RecycledContainers;
RecycledContainers mRecycledContainers;
// Generator status
GeneratorStatus mStatus;
bool mBatching;
// Generation state
GeneratorDirection mDirection;
uint32_t mNode; // node where next generation should occur
int mOffset; // offset inside the current node where next generation should occur
int mItemIndex; // index of next generated item
// Events
EventHandler mStatusChanged;
ItemsChangedEventHandler mItemsChanged;
// Stores a reference to the item in the container to speed up some searches
static const DependencyProperty* ContainerItemProperty;
// Optimization for ContainerFromIndex look-ups
mutable uint32_t mLastNode;
mutable int mLastIndex;
NS_DECLARE_REFLECTION(ItemContainerGenerator, BaseComponent)
};
NS_WARNING_POP
}
#include <NsGui/ItemContainerGenerator.inl>
#endif