diff --git a/Import/GacUI.cpp b/Import/GacUI.cpp index bce7df60..211dceae 100644 --- a/Import/GacUI.cpp +++ b/Import/GacUI.cpp @@ -3588,7 +3588,7 @@ GuiGraphicsResourceManager IGuiGraphicsRendererFactory* GuiGraphicsResourceManager::GetRendererFactory(const WString& elementTypeName) { vint index=rendererFactories.Keys().IndexOf(elementTypeName); - return index==-1?0:rendererFactories.Values().Get(index).Obj(); + return index==-1?nullptr:rendererFactories.Values().Get(index).Obj(); } GuiGraphicsResourceManager* guiGraphicsResourceManager=0; @@ -8618,6 +8618,733 @@ Helper Functions } } +/*********************************************************************** +.\GRAPHICSCOMPOSITION\GUIGRAPHICSRESPONSIVECOMPOSITION.CPP +***********************************************************************/ + +namespace vl +{ + namespace presentation + { + namespace compositions + { + using namespace collections; + using namespace controls; + +/*********************************************************************** +GuiResponsiveCompositionBase +***********************************************************************/ + + void GuiResponsiveCompositionBase::OnParentLineChanged() + { + GuiBoundsComposition::OnParentLineChanged(); + GuiResponsiveCompositionBase* responsive = nullptr; + { + auto parent = GetParent(); + while (parent) + { + if (responsive = dynamic_cast(parent)) + { + break; + } + parent = parent->GetParent(); + } + } + + if (responsiveParent != responsive) + { + if (responsiveParent) + { + responsiveParent->OnResponsiveChildRemoved(this); + responsiveParent->OnResponsiveChildLevelUpdated(); + } + responsiveParent = responsive; + if (responsiveParent) + { + responsiveParent->OnResponsiveChildInserted(this); + responsiveParent->OnResponsiveChildLevelUpdated(); + } + } + } + + void GuiResponsiveCompositionBase::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child) + { + } + + void GuiResponsiveCompositionBase::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child) + { + } + + void GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated() + { + if (responsiveParent) + { + responsiveParent->OnResponsiveChildLevelUpdated(); + } + } + + GuiResponsiveCompositionBase::GuiResponsiveCompositionBase() + { + SetMinSizeLimitation(LimitToElementAndChildren); + SetPreferredMinSize(Size(1, 1)); + } + + GuiResponsiveCompositionBase::~GuiResponsiveCompositionBase() + { + } + + ResponsiveDirection GuiResponsiveCompositionBase::GetDirection() + { + return direction; + } + + void GuiResponsiveCompositionBase::SetDirection(ResponsiveDirection value) + { + if (direction != value) + { + direction = value; + OnResponsiveChildLevelUpdated(); + } + } + +/*********************************************************************** +GuiResponsiveSharedCollection +***********************************************************************/ + + void GuiResponsiveSharedCollection::BeforeInsert(vint index, controls::GuiControl* const& value) + { + CHECK_ERROR(!value->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedCollection::BeforeInsert(vint, GuiResponsiveSharedCollection* const&)#Cannot insert a shared control that is currently in use."); + } + + void GuiResponsiveSharedCollection::AfterInsert(vint index, controls::GuiControl* const& value) + { + view->OnResponsiveChildLevelUpdated(); + } + + void GuiResponsiveSharedCollection::BeforeRemove(vint index, controls::GuiControl* const& value) + { + CHECK_ERROR(!value->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedCollection::BeforeRemove(vint, GuiResponsiveSharedCollection* const&)#Cannot remove a shared control that is currently in use."); + } + + void GuiResponsiveSharedCollection::AfterRemove(vint index, vint count) + { + view->OnResponsiveChildLevelUpdated(); + } + + GuiResponsiveSharedCollection::GuiResponsiveSharedCollection(GuiResponsiveViewComposition* _view) + :view(_view) + { + } + + GuiResponsiveSharedCollection::~GuiResponsiveSharedCollection() + { + } + +/*********************************************************************** +GuiResponsiveViewCollection +***********************************************************************/ + + void GuiResponsiveViewCollection::BeforeInsert(vint index, GuiResponsiveCompositionBase* const& value) + { + CHECK_ERROR(!value->GetParent(), L"GuiResponsiveViewCollection::BeforeRemove(vint, GuiResponsiveCompositionBase* const&)#Cannot insert a view that is currently in use."); + } + + void GuiResponsiveViewCollection::AfterInsert(vint index, GuiResponsiveCompositionBase* const& value) + { + if (!view->currentView) + { + view->skipUpdatingLevels = true; + view->currentView = value; + view->currentView->SetAlignmentToParent(Margin(0, 0, 0, 0)); + view->AddChild(view->currentView); + view->skipUpdatingLevels = false; + } + view->OnResponsiveChildLevelUpdated(); + } + + void GuiResponsiveViewCollection::BeforeRemove(vint index, GuiResponsiveCompositionBase* const& value) + { + CHECK_ERROR(!value->GetParent(), L"GuiResponsiveViewCollection::BeforeRemove(vint, GuiResponsiveCompositionBase* const&)#Cannot remove a view that is currently in use."); + } + + void GuiResponsiveViewCollection::AfterRemove(vint index, vint count) + { + view->OnResponsiveChildLevelUpdated(); + } + + GuiResponsiveViewCollection::GuiResponsiveViewCollection(GuiResponsiveViewComposition* _view) + :view(_view) + { + } + + GuiResponsiveViewCollection::~GuiResponsiveViewCollection() + { + } + +/*********************************************************************** +GuiResponsiveSharedComposition +***********************************************************************/ + + void GuiResponsiveSharedComposition::SetSharedControl() + { + if (shared && view && !view->destructing) + { + auto sharedParent = shared->GetBoundsComposition()->GetParent(); + CHECK_ERROR(view->sharedControls.Contains(shared), L"GuiResponsiveSharedComposition::SetSharedControl()#The specified shared control is not in GuiResponsiveViewComposition::GetSharedControls()."); + CHECK_ERROR(!sharedParent || sharedParent == this, L"GuiResponsiveSharedComposition::SetSharedControl()#The specified shared control has not been released. This usually means this control is not in GuiResponsiveViewComposition::GetSharedControls()."); + + if (!sharedParent) + { + shared->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0)); + AddChild(shared->GetBoundsComposition()); + view->usedSharedControls.Add(shared); + } + } + } + + void GuiResponsiveSharedComposition::OnParentLineChanged() + { + GuiBoundsComposition::OnParentLineChanged(); + if (view && view->destructing) + { + return; + } + + GuiResponsiveViewComposition* currentView = nullptr; + { + auto parent = GetParent(); + while (parent) + { + if (currentView = dynamic_cast(parent)) + { + break; + } + parent = parent->GetParent(); + } + } + + if (currentView != view && view && shared) + { + RemoveChild(shared->GetBoundsComposition()); + view->usedSharedControls.Remove(shared); + } + view = currentView; + + SetSharedControl(); + } + + GuiResponsiveSharedComposition::GuiResponsiveSharedComposition() + { + SetMinSizeLimitation(LimitToElementAndChildren); + } + + GuiResponsiveSharedComposition::~GuiResponsiveSharedComposition() + { + } + + controls::GuiControl* GuiResponsiveSharedComposition::GetShared() + { + return shared; + } + + void GuiResponsiveSharedComposition::SetShared(controls::GuiControl* value) + { + if (shared != value) + { + CHECK_ERROR(!shared || !shared->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedComposition::SetShared(GuiControl*)#Cannot replace a shared control that is currently in use."); + shared = value; + SetSharedControl(); + } + } + +/*********************************************************************** +GuiResponsiveViewComposition +***********************************************************************/ + + bool GuiResponsiveViewComposition::CalculateLevelCount() + { + vint old = levelCount; + if (views.Count() == 0) + { + levelCount = 1; + } + else + { + levelCount = 0; + for (vint i = 0; i < views.Count(); i++) + { + auto view = views[i]; + if (((vint)direction & (vint)view->GetDirection()) != 0) + { + levelCount += view->GetLevelCount(); + } + else + { + levelCount += 1; + } + } + } + + if (old != levelCount) + { + LevelCountChanged.Execute(GuiEventArgs(this)); + return true; + } + return false; + } + + bool GuiResponsiveViewComposition::CalculateCurrentLevel() + { + vint old = currentLevel; + currentLevel = 0; + for (vint i = views.Count() - 1; i >= 0; i--) + { + auto view = views[i]; + if (((vint)direction & (vint)view->GetDirection()) != 0) + { + if (currentView == view) + { + currentLevel += view->GetCurrentLevel() + 1; + break; + } + else + { + currentLevel += view->GetLevelCount(); + } + } + else + { + currentLevel++; + } + } + currentLevel--; + + if (old != currentLevel) + { + CurrentLevelChanged.Execute(GuiEventArgs(this)); + return true; + } + return false; + } + + void GuiResponsiveViewComposition::OnResponsiveChildLevelUpdated() + { + if (!skipUpdatingLevels) + { + CalculateLevelCount(); + CalculateCurrentLevel(); + GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated(); + } + } + + GuiResponsiveViewComposition::GuiResponsiveViewComposition() + :sharedControls(this) + , views(this) + { + } + + GuiResponsiveViewComposition::~GuiResponsiveViewComposition() + { + destructing = true; + + FOREACH(GuiResponsiveCompositionBase*, view, views) + { + if (view != currentView) + { + SafeDeleteComposition(view); + } + } + + FOREACH(GuiControl*, shared, From(sharedControls).Except(usedSharedControls)) + { + SafeDeleteControl(shared); + } + } + + vint GuiResponsiveViewComposition::GetLevelCount() + { + return levelCount; + } + + vint GuiResponsiveViewComposition::GetCurrentLevel() + { + return currentLevel; + } + + bool GuiResponsiveViewComposition::LevelDown() + { + skipUpdatingLevels = true; + if (((vint)direction & (vint)currentView->GetDirection()) != 0 && !currentView->LevelDown()) + { + vint index = views.IndexOf(currentView); + if (index < views.Count() - 1) + { + RemoveChild(currentView); + currentView = views[index + 1]; + currentView->SetAlignmentToParent(Margin(0, 0, 0, 0)); + AddChild(currentView); + } + } + skipUpdatingLevels = false; + return CalculateCurrentLevel(); + } + + bool GuiResponsiveViewComposition::LevelUp() + { + skipUpdatingLevels = true; + if (((vint)direction & (vint)currentView->GetDirection()) != 0 && !currentView->LevelUp()) + { + vint index = views.IndexOf(currentView); + if (index > 0) + { + RemoveChild(currentView); + currentView = views[index - 1]; + currentView->SetAlignmentToParent(Margin(0, 0, 0, 0)); + AddChild(currentView); + } + } + skipUpdatingLevels = false; + return CalculateCurrentLevel(); + } + + collections::ObservableListBase& GuiResponsiveViewComposition::GetSharedControls() + { + return sharedControls; + } + + collections::ObservableListBase& GuiResponsiveViewComposition::GetViews() + { + return views; + } + +/*********************************************************************** +GuiResponsiveFixedComposition +***********************************************************************/ + + void GuiResponsiveFixedComposition::OnResponsiveChildLevelUpdated() + { + } + + GuiResponsiveFixedComposition::GuiResponsiveFixedComposition() + { + } + + GuiResponsiveFixedComposition::~GuiResponsiveFixedComposition() + { + } + + vint GuiResponsiveFixedComposition::GetLevelCount() + { + return 1; + } + + vint GuiResponsiveFixedComposition::GetCurrentLevel() + { + return 0; + } + + bool GuiResponsiveFixedComposition::LevelDown() + { + return false; + } + + bool GuiResponsiveFixedComposition::LevelUp() + { + return false; + } + +/*********************************************************************** +GuiResponsiveStackComposition +***********************************************************************/ + +#define DEFINE_AVAILABLE \ + auto availables = From(responsiveChildren) \ + .Where([=](GuiResponsiveCompositionBase* child) \ + { \ + return ((vint)direction & (vint)child->GetDirection()) != 0; \ + }) \ + + bool GuiResponsiveStackComposition::CalculateLevelCount() + { + vint old = levelCount; + DEFINE_AVAILABLE; + if (availables.IsEmpty()) + { + levelCount = 1; + } + else + { + levelCount = availables + .Select([](GuiResponsiveCompositionBase* child) + { + return child->GetLevelCount() - 1; + }) + .Aggregate([](vint a, vint b) + { + return a + b; + }) + 1; + } + + if (old != levelCount) + { + LevelCountChanged.Execute(GuiEventArgs(this)); + return true; + } + return false; + } + + bool GuiResponsiveStackComposition::CalculateCurrentLevel() + { + vint old = currentLevel; + DEFINE_AVAILABLE; + if (availables.IsEmpty()) + { + currentLevel = 0; + } + else + { + currentLevel = availables + .Select([](GuiResponsiveCompositionBase* child) + { + return child->GetCurrentLevel(); + }) + .Aggregate([](vint a, vint b) + { + return a + b; + }); + } + + if (old != currentLevel) + { + CurrentLevelChanged.Execute(GuiEventArgs(this)); + return true; + } + return false; + } + + void GuiResponsiveStackComposition::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child) + { + responsiveChildren.Add(child); + } + + void GuiResponsiveStackComposition::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child) + { + responsiveChildren.Remove(child); + } + + void GuiResponsiveStackComposition::OnResponsiveChildLevelUpdated() + { + CalculateLevelCount(); + CalculateCurrentLevel(); + GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated(); + } + + bool GuiResponsiveStackComposition::ChangeLevel(bool levelDown) + { + DEFINE_AVAILABLE; + + SortedList ignored; + while (true) + { + GuiResponsiveCompositionBase* selected = false; + vint size = 0; + + FOREACH(GuiResponsiveCompositionBase*, child, availables) + { + if (!ignored.Contains(child)) + { + Size childSize = child->GetPreferredBounds().GetSize(); + vint childSizeToCompare = + direction == ResponsiveDirection::Horizontal ? childSize.x : + direction == ResponsiveDirection::Vertical ? childSize.y : + childSize.x * childSize.y; + + if (!selected || (levelDown ? size < childSizeToCompare : size > childSizeToCompare)) + { + selected = child; + size = childSizeToCompare; + } + } + } + + if (!selected) + { + break; + } + else if (levelDown ? selected->LevelDown() : selected->LevelUp()) + { + break; + } + else + { + ignored.Add(selected); + } + } + + return CalculateCurrentLevel(); + } + + GuiResponsiveStackComposition::GuiResponsiveStackComposition() + { + } + + GuiResponsiveStackComposition::~GuiResponsiveStackComposition() + { + } + + vint GuiResponsiveStackComposition::GetLevelCount() + { + return levelCount; + } + + vint GuiResponsiveStackComposition::GetCurrentLevel() + { + return currentLevel; + } + + bool GuiResponsiveStackComposition::LevelDown() + { + return ChangeLevel(true); + } + + bool GuiResponsiveStackComposition::LevelUp() + { + return ChangeLevel(false); + } + +/*********************************************************************** +GuiResponsiveGroupComposition +***********************************************************************/ + + bool GuiResponsiveGroupComposition::CalculateLevelCount() + { + vint old = levelCount; + DEFINE_AVAILABLE; + if (availables.IsEmpty()) + { + levelCount = 1; + } + else + { + levelCount = availables + .Select([](GuiResponsiveCompositionBase* child) + { + return child->GetLevelCount(); + }) + .Max(); + } + + if (old != levelCount) + { + LevelCountChanged.Execute(GuiEventArgs(this)); + return true; + } + return false; + } + + bool GuiResponsiveGroupComposition::CalculateCurrentLevel() + { + vint old = currentLevel; + DEFINE_AVAILABLE; + if (availables.IsEmpty()) + { + currentLevel = 0; + } + else + { + currentLevel = availables + .Select([](GuiResponsiveCompositionBase* child) + { + return child->GetCurrentLevel(); + }) + .Max(); + } + + if (old != currentLevel) + { + CurrentLevelChanged.Execute(GuiEventArgs(this)); + return true; + } + return false; + } + + void GuiResponsiveGroupComposition::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child) + { + responsiveChildren.Add(child); + } + + void GuiResponsiveGroupComposition::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child) + { + responsiveChildren.Remove(child); + } + + void GuiResponsiveGroupComposition::OnResponsiveChildLevelUpdated() + { + CalculateLevelCount(); + CalculateCurrentLevel(); + GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated(); + } + + GuiResponsiveGroupComposition::GuiResponsiveGroupComposition() + { + } + + GuiResponsiveGroupComposition::~GuiResponsiveGroupComposition() + { + } + + vint GuiResponsiveGroupComposition::GetLevelCount() + { + return levelCount; + } + + vint GuiResponsiveGroupComposition::GetCurrentLevel() + { + return currentLevel; + } + + bool GuiResponsiveGroupComposition::LevelDown() + { + DEFINE_AVAILABLE; + vint level = currentLevel; + FOREACH(GuiResponsiveCompositionBase*, child, availables) + { + if (child->GetCurrentLevel() >= level) + { + if (!child->LevelDown()) + { + break; + } + } + } + + return CalculateCurrentLevel(); + } + + bool GuiResponsiveGroupComposition::LevelUp() + { + DEFINE_AVAILABLE; + vint level = currentLevel; + FOREACH(GuiResponsiveCompositionBase*, child, availables) + { + while (child->GetCurrentLevel() <= level) + { + if (!child->LevelUp()) + { + break; + } + } + } + + return CalculateCurrentLevel(); + } + +#undef DEFINE_AVAILABLE + } + } +} + + /*********************************************************************** .\CONTROLS\GUIAPPLICATION.CPP ***********************************************************************/ diff --git a/Import/GacUI.h b/Import/GacUI.h index 8c5d63ac..b841b362 100644 --- a/Import/GacUI.h +++ b/Import/GacUI.h @@ -7182,6 +7182,232 @@ Flow Compositions #endif +/*********************************************************************** +.\GRAPHICSCOMPOSITION\GUIGRAPHICSRESPONSIVECOMPOSITION.H +***********************************************************************/ +/*********************************************************************** +Vczh Library++ 3.0 +Developer: Zihan Chen(vczh) +GacUI::Composition System + +Interfaces: +***********************************************************************/ + +#ifndef VCZH_PRESENTATION_ELEMENTS_GUIGRAPHICSRESPONSIVECOMPOSITION +#define VCZH_PRESENTATION_ELEMENTS_GUIGRAPHICSRESPONSIVECOMPOSITION + + +namespace vl +{ + namespace presentation + { + namespace compositions + { + +/*********************************************************************** +GuiResponsiveCompositionBase +***********************************************************************/ + + enum class ResponsiveDirection + { + Horizontal = 1, + Vertical = 2, + Both = 3, + }; + + /// Base class for responsive layout compositions. + class GuiResponsiveCompositionBase abstract : public GuiBoundsComposition, public Description + { + protected: + GuiResponsiveCompositionBase* responsiveParent = nullptr; + ResponsiveDirection direction = ResponsiveDirection::Both; + + void OnParentLineChanged()override; + virtual void OnResponsiveChildInserted(GuiResponsiveCompositionBase* child); + virtual void OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child); + virtual void OnResponsiveChildLevelUpdated(); + + public: + GuiResponsiveCompositionBase(); + ~GuiResponsiveCompositionBase(); + + GuiNotifyEvent LevelCountChanged; + GuiNotifyEvent CurrentLevelChanged; + + virtual vint GetLevelCount() = 0; + virtual vint GetCurrentLevel() = 0; + virtual bool LevelDown() = 0; + virtual bool LevelUp() = 0; + + ResponsiveDirection GetDirection(); + void SetDirection(ResponsiveDirection value); + }; + +/*********************************************************************** +GuiResponsiveViewComposition +***********************************************************************/ + + class GuiResponsiveViewComposition; + class GuiResponsiveSharedComposition; + + class GuiResponsiveSharedCollection : public collections::ObservableListBase + { + protected: + GuiResponsiveViewComposition* view = nullptr; + + void BeforeInsert(vint index, controls::GuiControl* const& value)override; + void AfterInsert(vint index, controls::GuiControl* const& value)override; + void BeforeRemove(vint index, controls::GuiControl* const& value)override; + void AfterRemove(vint index, vint count)override; + + public: + GuiResponsiveSharedCollection(GuiResponsiveViewComposition* _view); + ~GuiResponsiveSharedCollection(); + }; + + class GuiResponsiveViewCollection : public collections::ObservableListBase + { + protected: + GuiResponsiveViewComposition* view = nullptr; + + void BeforeInsert(vint index, GuiResponsiveCompositionBase* const& value)override; + void AfterInsert(vint index, GuiResponsiveCompositionBase* const& value)override; + void BeforeRemove(vint index, GuiResponsiveCompositionBase* const& value)override; + void AfterRemove(vint index, vint count)override; + + public: + GuiResponsiveViewCollection(GuiResponsiveViewComposition* _view); + ~GuiResponsiveViewCollection(); + }; + + class GuiResponsiveSharedComposition : public GuiBoundsComposition, public Description + { + protected: + GuiResponsiveViewComposition* view = nullptr; + controls::GuiControl* shared = nullptr; + + void SetSharedControl(); + void OnParentLineChanged()override; + + public: + GuiResponsiveSharedComposition(); + ~GuiResponsiveSharedComposition(); + + controls::GuiControl* GetShared(); + void SetShared(controls::GuiControl* value); + }; + + /// A responsive layout composition defined by views of different sizes. + class GuiResponsiveViewComposition : public GuiResponsiveCompositionBase, public Description + { + friend class GuiResponsiveSharedCollection; + friend class GuiResponsiveViewCollection; + friend class GuiResponsiveSharedComposition; + using ControlSet = collections::SortedList; + protected: + vint levelCount = 1; + vint currentLevel = 0; + bool skipUpdatingLevels = false; + GuiResponsiveCompositionBase* currentView = nullptr; + + ControlSet usedSharedControls; + GuiResponsiveSharedCollection sharedControls; + GuiResponsiveViewCollection views; + bool destructing = false; + + bool CalculateLevelCount(); + bool CalculateCurrentLevel(); + void OnResponsiveChildLevelUpdated(); + + public: + GuiResponsiveViewComposition(); + ~GuiResponsiveViewComposition(); + + vint GetLevelCount()override; + vint GetCurrentLevel()override; + bool LevelDown()override; + bool LevelUp()override; + + collections::ObservableListBase& GetSharedControls(); + collections::ObservableListBase& GetViews(); + }; + +/*********************************************************************** +Others +***********************************************************************/ + + /// A responsive layout composition which stop parent responsive composition to search its children. + class GuiResponsiveFixedComposition : public GuiResponsiveCompositionBase, public Description + { + protected: + void OnResponsiveChildLevelUpdated()override; + + public: + GuiResponsiveFixedComposition(); + ~GuiResponsiveFixedComposition(); + + vint GetLevelCount()override; + vint GetCurrentLevel()override; + bool LevelDown()override; + bool LevelUp()override; + }; + + /// A responsive layout composition which change its size by changing children's views one by one in one direction. + class GuiResponsiveStackComposition : public GuiResponsiveCompositionBase, public Description + { + using ResponsiveChildList = collections::List; + protected: + vint levelCount = 1; + vint currentLevel = 0; + ResponsiveChildList responsiveChildren; + + bool CalculateLevelCount(); + bool CalculateCurrentLevel(); + void OnResponsiveChildInserted(GuiResponsiveCompositionBase* child); + void OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child); + void OnResponsiveChildLevelUpdated(); + bool ChangeLevel(bool levelDown); + + public: + GuiResponsiveStackComposition(); + ~GuiResponsiveStackComposition(); + + vint GetLevelCount()override; + vint GetCurrentLevel()override; + bool LevelDown()override; + bool LevelUp()override; + }; + + /// A responsive layout composition which change its size by changing children's views at the same time. + class GuiResponsiveGroupComposition : public GuiResponsiveCompositionBase, public Description + { + using ResponsiveChildList = collections::List; + protected: + vint levelCount = 1; + vint currentLevel = 0; + ResponsiveChildList responsiveChildren; + + bool CalculateLevelCount(); + bool CalculateCurrentLevel(); + void OnResponsiveChildInserted(GuiResponsiveCompositionBase* child); + void OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child); + void OnResponsiveChildLevelUpdated(); + + public: + GuiResponsiveGroupComposition(); + ~GuiResponsiveGroupComposition(); + + vint GetLevelCount()override; + vint GetCurrentLevel()override; + bool LevelDown()override; + bool LevelUp()override; + }; + } + } +} + +#endif + /*********************************************************************** .\GRAPHICSCOMPOSITION\GUIGRAPHICSSPECIALIZEDCOMPOSITION.H ***********************************************************************/ diff --git a/Import/GacUICompiler.cpp b/Import/GacUICompiler.cpp index 7e47bc82..baa282a5 100644 --- a/Import/GacUICompiler.cpp +++ b/Import/GacUICompiler.cpp @@ -11011,6 +11011,8 @@ GuiInstanceLoader_Compositions.cpp Rows, Columns: array(GuiCellOption) GuiCellComposition Site: SiteValue + GuiResponsiveSharedComposition + ctor: Shared GuiInstanceLoader_Document.cpp GuiDocumentItem default: GuiControl*, GuiGraphicsComposition* diff --git a/Import/GacUIReflection.cpp b/Import/GacUIReflection.cpp index dd2bcf74..4de759c3 100644 --- a/Import/GacUIReflection.cpp +++ b/Import/GacUIReflection.cpp @@ -814,6 +814,7 @@ namespace vl using namespace collections; using namespace presentation; using namespace presentation::compositions; + using namespace presentation::controls; #ifndef VCZH_DEBUG_NO_REFLECTION @@ -1157,6 +1158,51 @@ Type Declaration CLASS_MEMBER_CONSTRUCTOR(GuiRepeatFlowComposition*(), NO_PARAMETER) END_CLASS_MEMBER(GuiRepeatFlowComposition) + BEGIN_ENUM_ITEM(ResponsiveDirection) + ENUM_CLASS_ITEM(Horizontal) + ENUM_CLASS_ITEM(Vertical) + ENUM_CLASS_ITEM(Both) + END_ENUM_ITEM(ResponsiveDirection) + + BEGIN_CLASS_MEMBER(GuiResponsiveCompositionBase) + CLASS_MEMBER_BASE(GuiBoundsComposition) + + CLASS_MEMBER_PROPERTY_GUIEVENT_READONLY_FAST(LevelCount) + CLASS_MEMBER_PROPERTY_GUIEVENT_READONLY_FAST(CurrentLevel) + CLASS_MEMBER_PROPERTY_FAST(Direction) + CLASS_MEMBER_METHOD(LevelDown, NO_PARAMETER) + CLASS_MEMBER_METHOD(LevelUp, NO_PARAMETER) + END_CLASS_MEMBER(GuiResponsiveCompositionBase) + + BEGIN_CLASS_MEMBER(GuiResponsiveSharedComposition) + CLASS_MEMBER_BASE(GuiBoundsComposition) + CLASS_MEMBER_CONSTRUCTOR(GuiResponsiveSharedComposition*(), NO_PARAMETER) + CLASS_MEMBER_PROPERTY_FAST(Shared) + END_CLASS_MEMBER(GuiResponsiveSharedComposition) + + BEGIN_CLASS_MEMBER(GuiResponsiveViewComposition) + CLASS_MEMBER_BASE(GuiResponsiveCompositionBase) + CLASS_MEMBER_CONSTRUCTOR(GuiResponsiveViewComposition*(), NO_PARAMETER) + + CLASS_MEMBER_PROPERTY_READONLY_FAST(SharedControls) + CLASS_MEMBER_PROPERTY_READONLY_FAST(Views) + END_CLASS_MEMBER(GuiResponsiveViewComposition) + + BEGIN_CLASS_MEMBER(GuiResponsiveFixedComposition) + CLASS_MEMBER_BASE(GuiResponsiveCompositionBase) + CLASS_MEMBER_CONSTRUCTOR(GuiResponsiveFixedComposition*(), NO_PARAMETER) + END_CLASS_MEMBER(GuiResponsiveFixedComposition) + + BEGIN_CLASS_MEMBER(GuiResponsiveStackComposition) + CLASS_MEMBER_BASE(GuiResponsiveCompositionBase) + CLASS_MEMBER_CONSTRUCTOR(GuiResponsiveStackComposition*(), NO_PARAMETER) + END_CLASS_MEMBER(GuiResponsiveStackComposition) + + BEGIN_CLASS_MEMBER(GuiResponsiveGroupComposition) + CLASS_MEMBER_BASE(GuiResponsiveCompositionBase) + CLASS_MEMBER_CONSTRUCTOR(GuiResponsiveGroupComposition*(), NO_PARAMETER) + END_CLASS_MEMBER(GuiResponsiveGroupComposition) + BEGIN_INTERFACE_MEMBER_NOPROXY(IGuiShortcutKeyItem) CLASS_MEMBER_PROPERTY_READONLY_FAST(Manager) CLASS_MEMBER_PROPERTY_READONLY_FAST(Name) diff --git a/Import/GacUIReflection.h b/Import/GacUIReflection.h index 0ed0f5ec..899d6a33 100644 --- a/Import/GacUIReflection.h +++ b/Import/GacUIReflection.h @@ -227,6 +227,13 @@ Type List (Compositions) F(presentation::compositions::GuiRepeatCompositionBase)\ F(presentation::compositions::GuiRepeatStackComposition)\ F(presentation::compositions::GuiRepeatFlowComposition)\ + F(presentation::compositions::ResponsiveDirection)\ + F(presentation::compositions::GuiResponsiveCompositionBase)\ + F(presentation::compositions::GuiResponsiveSharedComposition)\ + F(presentation::compositions::GuiResponsiveViewComposition)\ + F(presentation::compositions::GuiResponsiveFixedComposition)\ + F(presentation::compositions::GuiResponsiveStackComposition)\ + F(presentation::compositions::GuiResponsiveGroupComposition)\ F(presentation::compositions::IGuiShortcutKeyItem)\ F(presentation::compositions::IGuiShortcutKeyManager)\ F(presentation::compositions::GuiShortcutKeyManager)\ diff --git a/Import/GacUIWindows.cpp b/Import/GacUIWindows.cpp index 1e9f2318..b7f21ae4 100644 --- a/Import/GacUIWindows.cpp +++ b/Import/GacUIWindows.cpp @@ -3384,7 +3384,7 @@ WindowsImageFrame Ptr WindowsImageFrame::GetCache(void* key) { vint index=caches.Keys().IndexOf(key); - return index==-1?0:caches.Values().Get(index); + return index==-1?nullptr:caches.Values().Get(index); } Ptr WindowsImageFrame::RemoveCache(void* key) diff --git a/Import/Vlpp.cpp b/Import/Vlpp.cpp index 627fbeb2..5ceb32fe 100644 --- a/Import/Vlpp.cpp +++ b/Import/Vlpp.cpp @@ -7,10 +7,10 @@ DEVELOPER: Zihan Chen(vczh) /*********************************************************************** .\BASIC.CPP ***********************************************************************/ +#include #if defined VCZH_MSVC #include #elif defined VCZH_GCC -#include #include #endif @@ -495,6 +495,11 @@ namespace vl buffer++; } } + + void wcscpy_s(wchar_t* buffer, size_t size, const wchar_t* text) + { + wcscpy(buffer, text); + } #endif vint atoi_test(const AString& string, bool& success) @@ -777,6 +782,156 @@ namespace vl _wcsupr_s((wchar_t*)result.Buffer(), result.Length() + 1); return result; } + + WString LoremIpsum(vint bestLength, LoremIpsumCasing casing) + { + static const wchar_t* words[] = + { + L"lorem", L"ipsum", L"dolor", L"sit", L"amet", L"consectetur", L"adipiscing", L"elit", L"integer", + L"nec", L"odio", L"praesent", L"libero", L"sed", L"cursus", L"ante", L"dapibus", L"diam", + L"sed", L"nisi", L"nulla", L"quis", L"sem", L"at", L"nibh", L"elementum", L"imperdiet", L"duis", + L"sagittis", L"ipsum", L"praesent", L"mauris", L"fusce", L"nec", L"tellus", L"sed", L"augue", + L"semper", L"porta", L"mauris", L"massa", L"vestibulum", L"lacinia", L"arcu", L"eget", L"nulla", + L"class", L"aptent", L"taciti", L"sociosqu", L"ad", L"litora", L"torquent", L"per", L"conubia", + L"nostra", L"per", L"inceptos", L"himenaeos", L"curabitur", L"sodales", L"ligula", L"in", + L"libero", L"sed", L"dignissim", L"lacinia", L"nunc", L"curabitur", L"tortor", L"pellentesque", + L"nibh", L"aenean", L"quam", L"in", L"scelerisque", L"sem", L"at", L"dolor", L"maecenas", + L"mattis", L"sed", L"convallis", L"tristique", L"sem", L"proin", L"ut", L"ligula", L"vel", + L"nunc", L"egestas", L"porttitor", L"morbi", L"lectus", L"risus", L"iaculis", L"vel", L"suscipit", + L"quis", L"luctus", L"non", L"massa", L"fusce", L"ac", L"turpis", L"quis", L"ligula", L"lacinia", + L"aliquet", L"mauris", L"ipsum", L"nulla", L"metus", L"metus", L"ullamcorper", L"vel", L"tincidunt", + L"sed", L"euismod", L"in", L"nibh", L"quisque", L"volutpat", L"condimentum", L"velit", L"class", + L"aptent", L"taciti", L"sociosqu", L"ad", L"litora", L"torquent", L"per", L"conubia", L"nostra", + L"per", L"inceptos", L"himenaeos", L"nam", L"nec", L"ante", L"sed", L"lacinia", L"urna", + L"non", L"tincidunt", L"mattis", L"tortor", L"neque", L"adipiscing", L"diam", L"a", L"cursus", + L"ipsum", L"ante", L"quis", L"turpis", L"nulla", L"facilisi", L"ut", L"fringilla", L"suspendisse", + L"potenti", L"nunc", L"feugiat", L"mi", L"a", L"tellus", L"consequat", L"imperdiet", L"vestibulum", + L"sapien", L"proin", L"quam", L"etiam", L"ultrices", L"suspendisse", L"in", L"justo", L"eu", + L"magna", L"luctus", L"suscipit", L"sed", L"lectus", L"integer", L"euismod", L"lacus", L"luctus", + L"magna", L"quisque", L"cursus", L"metus", L"vitae", L"pharetra", L"auctor", L"sem", L"massa", + L"mattis", L"sem", L"at", L"interdum", L"magna", L"augue", L"eget", L"diam", L"vestibulum", + L"ante", L"ipsum", L"primis", L"in", L"faucibus", L"orci", L"luctus", L"et", L"ultrices", + L"posuere", L"cubilia", L"curae;", L"morbi", L"lacinia", L"molestie", L"dui", L"praesent", + L"blandit", L"dolor", L"sed", L"non", L"quam", L"in", L"vel", L"mi", L"sit", L"amet", L"augue", + L"congue", L"elementum", L"morbi", L"in", L"ipsum", L"sit", L"amet", L"pede", L"facilisis", + L"laoreet", L"donec", L"lacus", L"nunc", L"viverra", L"nec", L"blandit", L"vel", L"egestas", + L"et", L"augue", L"vestibulum", L"tincidunt", L"malesuada", L"tellus", L"ut", L"ultrices", + L"ultrices", L"enim", L"curabitur", L"sit", L"amet", L"mauris", L"morbi", L"in", L"dui", + L"quis", L"est", L"pulvinar", L"ullamcorper", L"nulla", L"facilisi", L"integer", L"lacinia", + L"sollicitudin", L"massa", L"cras", L"metus", L"sed", L"aliquet", L"risus", L"a", L"tortor", + L"integer", L"id", L"quam", L"morbi", L"mi", L"quisque", L"nisl", L"felis", L"venenatis", + L"tristique", L"dignissim", L"in", L"ultrices", L"sit", L"amet", L"augue", L"proin", L"sodales", + L"libero", L"eget", L"ante", L"nulla", L"quam", L"aenean", L"laoreet", L"vestibulum", L"nisi", + L"lectus", L"commodo", L"ac", L"facilisis", L"ac", L"ultricies", L"eu", L"pede", L"ut", L"orci", + L"risus", L"accumsan", L"porttitor", L"cursus", L"quis", L"aliquet", L"eget", L"justo", + L"sed", L"pretium", L"blandit", L"orci", L"ut", L"eu", L"diam", L"at", L"pede", L"suscipit", + L"sodales", L"aenean", L"lectus", L"elit", L"fermentum", L"non", L"convallis", L"id", L"sagittis", + L"at", L"neque", L"nullam", L"mauris", L"orci", L"aliquet", L"et", L"iaculis", L"et", L"viverra", + L"vitae", L"ligula", L"nulla", L"ut", L"felis", L"in", L"purus", L"aliquam", L"imperdiet", + L"maecenas", L"aliquet", L"mollis", L"lectus", L"vivamus", L"consectetuer", L"risus", L"et", + L"tortor" + }; + static vint index = 0; + const vint WordCount = sizeof(words) / sizeof(*words); + + if (bestLength < 0) bestLength = 0; + vint bufferLength = bestLength + 20; + wchar_t* buffer = new wchar_t[bufferLength + 1]; + + buffer[0] = 0; + vint used = 0; + wchar_t* writing = buffer; + while (used < bestLength) + { + if (used != 0) + { + *writing++ = L' '; + used++; + } + + vint wordSize = (vint)wcslen(words[index]); + wcscpy_s(writing, bufferLength - used, words[index]); + if (casing == LoremIpsumCasing::AllWordsUpperCase || (casing == LoremIpsumCasing::FirstWordUpperCase && used == 0)) + { + *writing -= L'a' - L'A'; + } + + if (used != 0 && used + wordSize > bestLength) + { + vint deltaShort = bestLength - used + 1; + vint deltaLong = used + wordSize - bestLength; + if (deltaShort < deltaLong) + { + *--writing = 0; + used--; + break; + } + } + writing += wordSize; + used += wordSize; + index = (index + 1) % WordCount; + } + + WString result = buffer; + delete[] buffer; + return result; + } + + WString LoremIpsumTitle(vint bestLength) + { + return LoremIpsum(bestLength, LoremIpsumCasing::AllWordsUpperCase); + } + + WString LoremIpsumSentence(vint bestLength) + { + return LoremIpsum(bestLength, LoremIpsumCasing::FirstWordUpperCase) + L"."; + } + + WString LoremIpsumParagraph(vint bestLength) + { + srand((unsigned)time(0)); + auto casing = LoremIpsumCasing::FirstWordUpperCase; + vint comma = 0; + WString result; + while (result.Length() < bestLength) + { + vint offset = bestLength - result.Length(); + if (comma == 0) + { + comma = rand() % 4 + 1; + } + vint length = rand() % 45 + 15; + if (offset < 20) + { + comma = 0; + length = offset - 1; + } + else if (length > offset) + { + comma = 0; + length = offset + rand() % 11 - 5; + } + + result += LoremIpsum(length, casing); + if (comma == 0) + { + result += L"."; + break; + } + else if (comma == 1) + { + result += L". "; + casing = LoremIpsumCasing::FirstWordUpperCase; + } + else + { + result += L", "; + casing = LoremIpsumCasing::AllWordsLowerCase; + } + comma--; + } + return result; + } } @@ -4912,30 +5067,32 @@ Utilities } } - // concatincate response body - vint totalSize=0; - FOREACH(BufferPair, p, availableBuffers) { - totalSize+=p.length; - } - response.body.Resize(totalSize); - if(totalSize>0) - { - char* utf8=new char[totalSize]; + // concatincate response body + vint totalSize = 0; + FOREACH(BufferPair, p, availableBuffers) { - char* temp=utf8; - FOREACH(BufferPair, p, availableBuffers) - { - memcpy(temp, p.buffer, p.length); - temp+=p.length; - } + totalSize += p.length; + } + response.body.Resize(totalSize); + if (totalSize > 0) + { + char* utf8 = new char[totalSize]; + { + char* temp = utf8; + FOREACH(BufferPair, p, availableBuffers) + { + memcpy(temp, p.buffer, p.length); + temp += p.length; + } + } + memcpy(&response.body[0], utf8, totalSize); + delete[] utf8; + } + FOREACH(BufferPair, p, availableBuffers) + { + delete[] p.buffer; } - memcpy(&response.body[0], utf8, totalSize); - delete[] utf8; - } - FOREACH(BufferPair, p, availableBuffers) - { - delete[] p.buffer; } CLEANUP: if(requestInternet) WinHttpCloseHandle(requestInternet); diff --git a/Import/Vlpp.h b/Import/Vlpp.h index 30694d06..7fb8ab96 100644 --- a/Import/Vlpp.h +++ b/Import/Vlpp.h @@ -5835,7 +5835,20 @@ namespace vl extern void _strupr_s(char* buffer, size_t size); extern void _wcslwr_s(wchar_t* buffer, size_t size); extern void _wcsupr_s(wchar_t* buffer, size_t size); + extern void wcscpy_s(wchar_t* buffer, size_t size, const wchar_t* text); #endif + + enum class LoremIpsumCasing + { + AllWordsLowerCase, + FirstWordUpperCase, + AllWordsUpperCase, + }; + + extern WString LoremIpsum(vint bestLength, LoremIpsumCasing casing); + extern WString LoremIpsumTitle(vint bestLength); + extern WString LoremIpsumSentence(vint bestLength); + extern WString LoremIpsumParagraph(vint bestLength); } #endif diff --git a/Import/VlppWorkflowLibrary.cpp b/Import/VlppWorkflowLibrary.cpp index c782dd29..714dcceb 100644 --- a/Import/VlppWorkflowLibrary.cpp +++ b/Import/VlppWorkflowLibrary.cpp @@ -940,6 +940,11 @@ WfLoadLibraryTypes CLASS_MEMBER_STATIC_METHOD(Right, { L"value" _ L"length" }) CLASS_MEMBER_STATIC_METHOD(Mid, { L"value" _ L"start" _ L"length" }) CLASS_MEMBER_STATIC_METHOD(Find, { L"value" _ L"substr" }) + CLASS_MEMBER_STATIC_METHOD(UCase, { L"value" }) + CLASS_MEMBER_STATIC_METHOD(LCase, { L"value" }) + CLASS_MEMBER_STATIC_METHOD(LoremIpsumTitle, { L"bestLength" }) + CLASS_MEMBER_STATIC_METHOD(LoremIpsumSentence, { L"bestLength" }) + CLASS_MEMBER_STATIC_METHOD(LoremIpsumParagraph, { L"bestLength" }) CLASS_MEMBER_STATIC_METHOD(ReverseEnumerable, { L"value" }) #pragma push_macro("CompareString") #if defined CompareString diff --git a/Import/VlppWorkflowLibrary.h b/Import/VlppWorkflowLibrary.h index b2a2c151..b5d3c58e 100644 --- a/Import/VlppWorkflowLibrary.h +++ b/Import/VlppWorkflowLibrary.h @@ -560,6 +560,12 @@ Libraries static WString Right(const WString& value, vint length) { return value.Right(length); } static WString Mid(const WString& value, vint start, vint length) { return value.Sub(start, length); } static vint Find(const WString& value, const WString& substr) { return INVLOC.FindFirst(value, substr, Locale::Normalization::None).key; } + static WString UCase(const WString& value) { return wupper(value); } + static WString LCase(const WString& value) { return wlower(value); } + + static WString LoremIpsumTitle(vint bestLength) { return vl::LoremIpsumTitle(bestLength); } + static WString LoremIpsumSentence(vint bestLength) { return vl::LoremIpsumSentence(bestLength); } + static WString LoremIpsumParagraph(vint bestLength) { return vl::LoremIpsumParagraph(bestLength); } #define DEFINE_COMPARE(TYPE) static vint Compare(TYPE a, TYPE b); REFLECTION_PREDEFINED_PRIMITIVE_TYPES(DEFINE_COMPARE) diff --git a/Tools/CppMerge.exe b/Tools/CppMerge.exe index f8468407..3996cc02 100644 Binary files a/Tools/CppMerge.exe and b/Tools/CppMerge.exe differ diff --git a/Tools/GacGen32.exe b/Tools/GacGen32.exe index 9846a0e9..5ccad310 100644 Binary files a/Tools/GacGen32.exe and b/Tools/GacGen32.exe differ diff --git a/Tools/GacGen64.exe b/Tools/GacGen64.exe index 15f39f9a..1175e94b 100644 Binary files a/Tools/GacGen64.exe and b/Tools/GacGen64.exe differ diff --git a/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin b/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin index 7e57568a..cf14783f 100644 Binary files a/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin and b/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin differ