diff --git a/Import/GacUI.cpp b/Import/GacUI.cpp index 9e3b1935..4224db8a 100644 --- a/Import/GacUI.cpp +++ b/Import/GacUI.cpp @@ -9653,15 +9653,13 @@ GuiInstanceRootObject } }; - bool GuiInstanceRootObject::InstallTimerCallback(controls::GuiControlHost* controlHost) + void GuiInstanceRootObject::InstallTimerCallback(controls::GuiControlHost* controlHost) { if (!timerCallback) { timerCallback = new RootObjectTimerCallback(this, controlHost); controlHost->GetTimerManager()->AddCallback(timerCallback); - return true; } - return false; } bool GuiInstanceRootObject::UninstallTimerCallback(controls::GuiControlHost* controlHost) @@ -9688,14 +9686,12 @@ GuiInstanceRootObject if (controlHost) { - if (InstallTimerCallback(controlHost)) + InstallTimerCallback(controlHost); + FOREACH(Ptr, animation, runningAnimations) { - FOREACH(Ptr, animation, runningAnimations) - { - animation->Resume(); - } - StartPendingAnimations(); + animation->Resume(); } + StartPendingAnimations(); } } @@ -9852,15 +9848,28 @@ GuiInstanceRootObject if (auto controlHost = GetControlHostForInstance()) { - if (InstallTimerCallback(controlHost)) - { - StartPendingAnimations(); - } + InstallTimerCallback(controlHost); + StartPendingAnimations(); } - return true; } } + + bool GuiInstanceRootObject::KillAnimation(Ptr animation) + { + if (!animation) return false; + if (runningAnimations.Contains(animation.Obj())) + { + runningAnimations.Remove(animation.Obj()); + return true; + } + if (pendingAnimations.Contains(animation.Obj())) + { + pendingAnimations.Remove(animation.Obj()); + return true; + } + return false; + } } } } @@ -24350,63 +24359,300 @@ namespace vl { namespace controls { + using namespace collections; + using namespace reflection::description; /*********************************************************************** -GuiWaitAnimation +GuiTimedAnimation ***********************************************************************/ - GuiWaitAnimation::GuiWaitAnimation(vuint64_t _length) + class GuiTimedAnimation : public Object, public virtual IGuiAnimation { + protected: + DateTime startTime; + vuint64_t time; + bool running = false; - } - - GuiWaitAnimation::~GuiWaitAnimation() - { - } - - void GuiWaitAnimation::Start() - { - startTime = DateTime::LocalTime(); - running = true; - } - - void GuiWaitAnimation::Pause() - { - running = false; - auto currentTime = DateTime::LocalTime(); - auto delta = currentTime.totalMilliseconds - startTime.totalMilliseconds; - if (length > delta) + public: + GuiTimedAnimation() { - length -= delta; } - else + + ~GuiTimedAnimation() { - length = 0; } + + void Start()override + { + startTime = DateTime::LocalTime(); + time = 0; + running = true; + } + + void Pause()override + { + time = GetTime(); + running = false; + } + + void Resume()override + { + startTime = DateTime::LocalTime(); + running = true; + } + + vuint64_t GetTime() + { + if (running) + { + return time + (DateTime::LocalTime().totalMilliseconds - startTime.totalMilliseconds); + } + else + { + return time; + } + } + }; + +/*********************************************************************** +GuiFiniteAnimation +***********************************************************************/ + + class GuiFiniteAnimation : public GuiTimedAnimation + { + protected: + vuint64_t length = 0; + Func run; + + public: + GuiFiniteAnimation(const Func& _run, vuint64_t _length) + :run(_run) + , length(_length) + { + } + + ~GuiFiniteAnimation() + { + } + + void Run()override + { + auto currentTime = GetTime(); + if (currentTime < length && run) + { + run(currentTime); + } + } + + bool GetStopped()override + { + return GetTime() >= length; + } + }; + +/*********************************************************************** +GuiInfiniteAnimation +***********************************************************************/ + + class GuiInfiniteAnimation : public GuiTimedAnimation + { + protected: + Func run; + + public: + GuiInfiniteAnimation(const Func& _run) + :run(_run) + { + } + + ~GuiInfiniteAnimation() + { + } + + void Run()override + { + if (run) + { + run(GetTime()); + } + } + + bool GetStopped()override + { + return false; + } + }; + +/*********************************************************************** +IGuiAnimation +***********************************************************************/ + + Ptr IGuiAnimation::CreateAnimation(const Func& run, vuint64_t milliseconds) + { + return new GuiFiniteAnimation(run, milliseconds); } - void GuiWaitAnimation::Resume() + Ptr IGuiAnimation::CreateAnimation(const Func& run) { - startTime = DateTime::LocalTime(); - running = true; + return new GuiInfiniteAnimation(run); } - void GuiWaitAnimation::Run() +/*********************************************************************** +IGuiAnimationCoroutine +***********************************************************************/ + + class GuiCoroutineAnimation : public Object, public virtual IGuiAnimationCoroutine::IImpl { + protected: + IGuiAnimationCoroutine::Creator creator; + Ptr coroutine; + + Ptr waitingAnimation; + vint waitingGroup = -1; + Group> groupAnimations; + + public: + GuiCoroutineAnimation(const IGuiAnimationCoroutine::Creator& _creator) + :creator(_creator) + { + } + + ~GuiCoroutineAnimation() + { + } + + void OnPlayAndWait(Ptr animation)override + { + CHECK_ERROR(!waitingAnimation && waitingGroup == -1, L"GuiCoroutineAnimation::OnPlayAndWait(Ptr)#Cannot be called when an animation or a group has already been waiting for."); + waitingAnimation = animation; + waitingAnimation->Start(); + } + + void OnPlayInGroup(Ptr animation, vint groupId)override + { + groupAnimations.Add(groupId, animation); + animation->Start(); + } + + void OnWaitForGroup(vint groupId)override + { + CHECK_ERROR(!waitingAnimation && waitingGroup == -1, L"GuiCoroutineAnimation::OnWaitForGroup(vint)#Cannot be called when an animation or a group has already been waiting for."); + if (groupAnimations.Keys().Contains(groupId)) + { + waitingGroup = groupId; + } + } + + void Start()override + { + CHECK_ERROR(!coroutine, L"GuiCoroutineAnimation::Start()#Cannot be called more than once."); + coroutine = creator(this); + } + + void Pause()override + { + if (waitingAnimation) + { + waitingAnimation->Pause(); + } + for (vint i = 0; i < groupAnimations.Count(); i++) + { + FOREACH(Ptr, animation, groupAnimations.GetByIndex(i)) + { + animation->Pause(); + } + } + } + + void Resume()override + { + if (waitingAnimation) + { + waitingAnimation->Resume(); + } + for (vint i = 0; i < groupAnimations.Count(); i++) + { + FOREACH(Ptr, animation, groupAnimations.GetByIndex(i)) + { + animation->Resume(); + } + } + } + + void Run()override + { + CHECK_ERROR(coroutine, L"GuiCoroutineAnimation::Run()#Cannot be called before calling Start."); + + if (waitingAnimation) + { + waitingAnimation->Run(); + if (waitingAnimation->GetStopped()) + { + waitingAnimation = nullptr; + } + } + + for (vint i = groupAnimations.Count() - 1; i >= 0; i--) + { + auto& animations = groupAnimations.GetByIndex(i); + for (vint j = animations.Count() - 1; j >= 0; j--) + { + auto animation = animations[j]; + animation->Run(); + if (animation->GetStopped()) + { + groupAnimations.Remove(i, animation.Obj()); + } + } + } + + if (waitingGroup != -1 && !groupAnimations.Keys().Contains(waitingGroup)) + { + waitingGroup = -1; + } + + if (coroutine->GetStatus() == CoroutineStatus::Waiting) + { + if (waitingAnimation || waitingGroup != -1) + { + return; + } + coroutine->Resume(true, nullptr); + } + } + + bool GetStopped()override + { + if (!coroutine) return false; + if (coroutine->GetStatus() != CoroutineStatus::Stopped) return false; + if (waitingAnimation || groupAnimations.Count() > 0) return false; + return true; + } + }; + + void IGuiAnimationCoroutine::WaitAndPause(IImpl* impl, vuint64_t milliseconds) + { + return PlayAndWaitAndPause(impl, IGuiAnimation::CreateAnimation({}, milliseconds)); } - bool GuiWaitAnimation::GetStopped() + void IGuiAnimationCoroutine::PlayAndWaitAndPause(IImpl* impl, Ptr animation) { - if (running) - { - auto currentTime = DateTime::LocalTime(); - auto delta = currentTime.totalMilliseconds - startTime.totalMilliseconds; - return length <= delta; - } - else - { - return length == 0; - } + impl->OnPlayAndWait(animation); + } + + void IGuiAnimationCoroutine::PlayInGroupAndPause(IImpl* impl, Ptr animation, vint groupId) + { + impl->OnPlayInGroup(animation, groupId); + } + + void IGuiAnimationCoroutine::WaitForGroupAndPause(IImpl* impl, vint groupId) + { + impl->OnWaitForGroup(groupId); + } + + Ptr IGuiAnimationCoroutine::Create(const Creator& creator) + { + return new GuiCoroutineAnimation(creator); } } } diff --git a/Import/GacUI.h b/Import/GacUI.h index 25562d3a..c210e398 100644 --- a/Import/GacUI.h +++ b/Import/GacUI.h @@ -1121,10 +1121,7 @@ Developer: Zihan Chen(vczh) GacUI::Native Window Interfaces: - INativeWindow :窗口适配器 - INativeWindowListener :窗口事件监听器 - INativeController :全局控制器 - INativeControllerListener :全局事件监听器 + INativeController : Interface for Operating System abstraction Renderers: GUI_GRAPHICS_RENDERER_GDI @@ -8126,6 +8123,12 @@ Animation /// Returns true if the animation has ended. virtual bool GetStopped() = 0; + + /// Create a finite animation. + static Ptr CreateAnimation(const Func& run, vuint64_t milliseconds); + + /// Create an infinite animation. + static Ptr CreateAnimation(const Func& run); }; /*********************************************************************** @@ -8149,7 +8152,7 @@ Root Object bool finalized = false; virtual controls::GuiControlHost* GetControlHostForInstance() = 0; - bool InstallTimerCallback(controls::GuiControlHost* controlHost); + void InstallTimerCallback(controls::GuiControlHost* controlHost); bool UninstallTimerCallback(controls::GuiControlHost* controlHost); void OnControlHostForInstanceChanged(); void StartPendingAnimations(); @@ -8200,6 +8203,11 @@ Root Object /// Returns true if this operation succeeded. /// The animation. bool AddAnimation(Ptr animation); + + /// Kill an animation. + /// Returns true if this operation succeeded. + /// The animation. + bool KillAnimation(Ptr animation); }; } } @@ -8228,22 +8236,24 @@ namespace vl { namespace controls { - class GuiWaitAnimation abstract : public virtual IGuiAnimation, public Description + class IGuiAnimationCoroutine : public Object, public Description { - protected: - DateTime startTime; - vuint64_t length = 0; - bool running = false; - public: - GuiWaitAnimation(vuint64_t _length); - ~GuiWaitAnimation(); + class IImpl : public virtual IGuiAnimation, public Description + { + public: + virtual void OnPlayAndWait(Ptr animation) = 0; + virtual void OnPlayInGroup(Ptr animation, vint groupId) = 0; + virtual void OnWaitForGroup(vint groupId) = 0; + }; - void Start()override; - void Pause()override; - void Resume()override; - void Run()override; - bool GetStopped()override; + typedef Func(IImpl*)> Creator; + + static void WaitAndPause(IImpl* impl, vuint64_t milliseconds); + static void PlayAndWaitAndPause(IImpl* impl, Ptr animation); + static void PlayInGroupAndPause(IImpl* impl, Ptr animation, vint groupId); + static void WaitForGroupAndPause(IImpl* impl, vint groupId); + static Ptr Create(const Creator& creator); }; } } diff --git a/Import/GacUICompiler.cpp b/Import/GacUICompiler.cpp index 862942ba..895fd941 100644 --- a/Import/GacUICompiler.cpp +++ b/Import/GacUICompiler.cpp @@ -316,1435 +316,6 @@ namespace vl } -/*********************************************************************** -.\GUIINSTANCEHELPERTYPES.CPP -***********************************************************************/ - -namespace vl -{ - namespace presentation - { - namespace helper_types - { - } - } - -#ifndef VCZH_DEBUG_NO_REFLECTION - - namespace reflection - { - namespace description - { - using namespace presentation::helper_types; - -/*********************************************************************** -Type Declaration -***********************************************************************/ - - GUIREFLECTIONHELPERTYPES_TYPELIST(IMPL_VL_TYPE_INFO) - -#define _ , - - BEGIN_STRUCT_MEMBER(SiteValue) - STRUCT_MEMBER(row) - STRUCT_MEMBER(column) - STRUCT_MEMBER(rowSpan) - STRUCT_MEMBER(columnSpan) - END_STRUCT_MEMBER(SiteValue) - -#undef _ - } - } - - namespace presentation - { - using namespace reflection::description; - using namespace controls; - -/*********************************************************************** -Type Loader -***********************************************************************/ - - class GuiHelperTypesLoader : public Object, public ITypeLoader - { - public: - void Load(ITypeManager* manager) - { - GUIREFLECTIONHELPERTYPES_TYPELIST(ADD_TYPE_INFO) - } - - void Unload(ITypeManager* manager) - { - } - }; - -/*********************************************************************** -GuiHelperTypesLoaderPlugin -***********************************************************************/ - - class GuiHelperTypesLoaderPlugin : public Object, public IGuiPlugin - { - public: - - GUI_PLUGIN_NAME(GacUI_Instance_ReflectionHelper) - { - GUI_PLUGIN_DEPEND(GacUI_Instance_Reflection); - } - - void Load()override - { - ITypeManager* manager=GetGlobalTypeManager(); - if(manager) - { - Ptr loader=new GuiHelperTypesLoader; - manager->AddTypeLoader(loader); - } - } - - void Unload()override - { - } - }; - GUI_REGISTER_PLUGIN(GuiHelperTypesLoaderPlugin) - } -#endif -} - -/*********************************************************************** -.\GUIINSTANCESHAREDSCRIPT.CPP -***********************************************************************/ - -namespace vl -{ - namespace presentation - { - using namespace parsing::xml; - -/*********************************************************************** -GuiInstanceSharedScript -***********************************************************************/ - - Ptr GuiInstanceSharedScript::LoadFromXml(Ptr resource, Ptr xml, GuiResourceError::List& errors) - { - if (xml->rootElement->subNodes.Count() == 1) - { - if (auto cdata = xml->rootElement->subNodes[0].Cast()) - { - auto script = MakePtr(); - script->language = xml->rootElement->name.value; - script->code = cdata->content.value; - script->codePosition = { {resource},cdata->codeRange.start }; - script->codePosition.column += 9; // rootElement->codeRange.start }, L"Script should be contained in a CDATA section.")); - return nullptr; - } - - Ptr GuiInstanceSharedScript::SaveToXml() - { - auto cdata = MakePtr(); - cdata->content.value = code; - - auto xml = MakePtr(); - xml->name.value = language; - xml->subNodes.Add(cdata); - - return xml; - } - } -} - -/*********************************************************************** -.\GUIINSTANCEREPRESENTATION.CPP -***********************************************************************/ - -namespace vl -{ - namespace presentation - { - using namespace collections; - using namespace parsing; - using namespace parsing::xml; - using namespace templates; - using namespace stream; - -/*********************************************************************** -GuiValueRepr -***********************************************************************/ - - void GuiValueRepr::CloneBody(Ptr repr) - { - repr->fromStyle = fromStyle; - repr->tagPosition = tagPosition; - } - -/*********************************************************************** -GuiTextRepr -***********************************************************************/ - - Ptr GuiTextRepr::Clone() - { - auto repr = MakePtr(); - GuiValueRepr::CloneBody(repr); - repr->text = text; - return repr; - } - - void GuiTextRepr::FillXml(Ptr xml) - { - if (!fromStyle) - { - auto xmlText = MakePtr(); - xmlText->content.value = text; - xml->subNodes.Add(xmlText); - } - } - -/*********************************************************************** -GuiAttSetterRepr -***********************************************************************/ - - void GuiAttSetterRepr::CloneBody(Ptr repr) - { - GuiValueRepr::CloneBody(repr); - - FOREACH_INDEXER(GlobalStringKey, name, index, setters.Keys()) - { - auto src = setters.Values()[index]; - auto dst = MakePtr(); - - dst->binding = src->binding; - dst->attPosition = src->attPosition; - FOREACH(Ptr, value, src->values) - { - dst->values.Add(value->Clone()); - } - - repr->setters.Add(name, dst); - } - - FOREACH_INDEXER(GlobalStringKey, name, index, eventHandlers.Keys()) - { - auto src = eventHandlers.Values()[index]; - auto dst = MakePtr(); - - dst->binding = src->binding; - dst->value = src->value; - dst->fromStyle = src->fromStyle; - dst->attPosition = src->attPosition; - dst->valuePosition = src->valuePosition; - - repr->eventHandlers.Add(name, dst); - } - - FOREACH_INDEXER(GlobalStringKey, name, index, environmentVariables.Keys()) - { - auto src = environmentVariables.Values()[index]; - auto dst = MakePtr(); - - dst->value = src->value; - dst->fromStyle = src->fromStyle; - dst->attPosition = src->attPosition; - dst->valuePosition = src->valuePosition; - - repr->environmentVariables.Add(name, dst); - } - - repr->instanceName = instanceName; - } - - Ptr GuiAttSetterRepr::Clone() - { - auto repr = MakePtr(); - GuiAttSetterRepr::CloneBody(repr); - repr->fromStyle = fromStyle; - return repr; - } - - void GuiAttSetterRepr::FillXml(Ptr xml) - { - if (!fromStyle) - { - if (instanceName != GlobalStringKey::Empty) - { - auto attName = MakePtr(); - attName->name.value = L"ref.Name"; - attName->value.value = instanceName.ToString(); - xml->attributes.Add(attName); - } - - for (vint i = 0; i < setters.Count(); i++) - { - auto key = setters.Keys()[i]; - auto value = setters.Values()[i]; - if (key == GlobalStringKey::Empty) - { - FOREACH(Ptr, repr, value->values) - { - repr->FillXml(xml); - } - } - else if (From(value->values).Any([](Ptr value) {return !value->fromStyle; })) - { - bool containsElement = From(value->values) - .Any([](Ptr value) - { - return !value->fromStyle && !value.Cast(); - }); - - if (containsElement) - { - auto xmlProp = MakePtr(); - xmlProp->name.value = L"att." + key.ToString(); - if (value->binding != GlobalStringKey::Empty) - { - xmlProp->name.value += L"-" + value->binding.ToString(); - } - - FOREACH(Ptr, repr, value->values) - { - if (!repr.Cast()) - { - repr->FillXml(xmlProp); - } - } - xml->subNodes.Add(xmlProp); - } - else - { - FOREACH(Ptr, repr, value->values) - { - if (auto textRepr = repr.Cast()) - { - if (!textRepr->fromStyle) - { - auto att = MakePtr(); - att->name.value = key.ToString(); - if (value->binding != GlobalStringKey::Empty) - { - att->name.value += L"-" + value->binding.ToString(); - } - att->value.value = textRepr->text; - xml->attributes.Add(att); - break; - } - } - } - } - } - } - - for (vint i = 0; i < eventHandlers.Count(); i++) - { - auto key = eventHandlers.Keys()[i]; - auto value = eventHandlers.Values()[i]; - if (!value->fromStyle) - { - auto xmlEvent = MakePtr(); - xmlEvent->name.value = L"ev." + key.ToString(); - if (value->binding != GlobalStringKey::Empty) - { - xmlEvent->name.value += L"-" + value->binding.ToString(); - } - xml->subNodes.Add(xmlEvent); - - auto xmlText = MakePtr(); - xmlText->content.value = value->value; - xmlEvent->subNodes.Add(xmlText); - } - } - - for (vint i = 0; i < environmentVariables.Count(); i++) - { - auto key = environmentVariables.Keys()[i]; - auto value = environmentVariables.Values()[i]; - if (!value->fromStyle) - { - auto xmlEnvVar = MakePtr(); - xmlEnvVar->name.value = L"env." + key.ToString(); - xml->subNodes.Add(xmlEnvVar); - - auto xmlText = MakePtr(); - xmlText->content.value = value->value; - xmlEnvVar->subNodes.Add(xmlText); - } - } - } - } - -/*********************************************************************** -GuiConstructorRepr -***********************************************************************/ - - Ptr GuiConstructorRepr::Clone() - { - auto repr = MakePtr(); - GuiAttSetterRepr::CloneBody(repr); - repr->fromStyle = fromStyle; - repr->typeNamespace = typeNamespace; - repr->typeName = typeName; - repr->styleName = styleName; - return repr; - } - - void GuiConstructorRepr::FillXml(Ptr xml) - { - if (!fromStyle) - { - auto xmlCtor = MakePtr(); - if (typeNamespace == GlobalStringKey::Empty) - { - xmlCtor->name.value = typeName.ToString(); - } - else - { - xmlCtor->name.value = typeNamespace.ToString() + L":" + typeName.ToString(); - } - - if (styleName) - { - auto attStyle = MakePtr(); - attStyle->name.value = L"ref.Style"; - attStyle->value.value = styleName.Value(); - xml->attributes.Add(attStyle); - } - - GuiAttSetterRepr::FillXml(xmlCtor); - xml->subNodes.Add(xmlCtor); - } - } - -/*********************************************************************** -GuiInstanceContext -***********************************************************************/ - - void GuiInstanceContext::CollectDefaultAttributes(Ptr resource, GuiAttSetterRepr::ValueList& values, Ptr xml, GuiResourceError::List& errors) - { - if (auto parser = GetParserManager()->GetParser(L"INSTANCE-ELEMENT-NAME")) - { - // test if there is only one text value in the xml - if (xml->subNodes.Count() == 1) - { - if (Ptr text = xml->subNodes[0].Cast()) - { - Ptr value = new GuiTextRepr; - value->text = text->content.value; - value->tagPosition = { {resource},text->content.codeRange.start }; - values.Add(value); - } - else if (Ptr text = xml->subNodes[0].Cast()) - { - Ptr value = new GuiTextRepr; - value->text = text->content.value; - value->tagPosition = { {resource},text->content.codeRange.start }; - value->tagPosition.column += 9; // , element, XmlGetElements(xml)) - { - if(auto name = parser->Parse({ resource }, element->name.value, element->codeRange.start, errors)) - { - if (name->IsCtorName()) - { - // collect constructor values in the default attribute setter - auto ctor = LoadCtor(resource, element, errors); - if (ctor) - { - values.Add(ctor); - } - } - else if (!name->IsPropertyElementName() && !name->IsEventElementName()) - { - errors.Add(GuiResourceError({ {resource},element->codeRange.start }, L"Unknown element name: \"" + element->name.value + L"\".")); - } - } - } - } - } - - void GuiInstanceContext::CollectAttributes(Ptr resource, GuiAttSetterRepr::SetteValuerMap& setters, Ptr xml, GuiResourceError::List& errors) - { - if (auto parser = GetParserManager()->GetParser(L"INSTANCE-ELEMENT-NAME")) - { - Ptr defaultValue = new GuiAttSetterRepr::SetterValue; - - // collect default attributes - CollectDefaultAttributes(resource, defaultValue->values, xml, errors); - if (defaultValue->values.Count() > 0) - { - setters.Add(GlobalStringKey::Empty, defaultValue); - } - - // collect values - FOREACH(Ptr, element, XmlGetElements(xml)) - { - if(auto name = parser->Parse({ resource }, element->name.value, element->name.codeRange.start, errors)) - { - if (name->IsPropertyElementName()) - { - // collect a value as a new attribute setter - if (setters.Keys().Contains(GlobalStringKey::Get(name->name))) - { - errors.Add(GuiResourceError({ {resource},element->codeRange.start }, L"Duplicated property \"" + name->name + L"\".")); - } - else - { - Ptr sv = new GuiAttSetterRepr::SetterValue; - sv->binding = GlobalStringKey::Get(name->binding); - sv->attPosition = { {resource},element->codeRange.start }; - - if (name->binding == L"set") - { - // if the binding is "set", it means that this element is a complete setter element - Ptr setter = new GuiAttSetterRepr; - FillAttSetter(resource, setter, element, errors); - sv->values.Add(setter); - } - else - { - // if the binding is not "set", then this is a single-value attribute or a colection attribute - // fill all data into this attribute - CollectDefaultAttributes(resource, sv->values, element, errors); - } - - if (sv->values.Count() > 0) - { - setters.Add(GlobalStringKey::Get(name->name), sv); - } - } - } - } - } - } - } - - void GuiInstanceContext::CollectEvents(Ptr resource, GuiAttSetterRepr::EventHandlerMap& eventHandlers, Ptr xml, GuiResourceError::List& errors) - { - if (auto parser = GetParserManager()->GetParser(L"INSTANCE-ELEMENT-NAME")) - { - // collect values - FOREACH(Ptr, element, XmlGetElements(xml)) - { - if(auto name = parser->Parse({ resource }, element->name.value, element->name.codeRange.start, errors)) - { - if (name->IsEventElementName()) - { - // collect a value as an event setter - if (eventHandlers.Keys().Contains(GlobalStringKey::Get(name->name))) - { - errors.Add(GuiResourceError({ {resource},element->codeRange.start }, L"Duplicated event \"" + name->name + L"\".")); - } - else - { - // test if there is only one text value in the xml - if (element->subNodes.Count() == 1) - { - if (Ptr text = element->subNodes[0].Cast()) - { - auto value = MakePtr(); - value->binding = GlobalStringKey::Get(name->binding); - value->value = text->content.value; - value->attPosition = { {resource},element->codeRange.start }; - value->valuePosition = { {resource},text->content.codeRange.start }; - eventHandlers.Add(GlobalStringKey::Get(name->name), value); - if (text->content.codeRange.start.row != text->content.codeRange.end.row) - { - errors.Add(GuiResourceError({ {resource},element->codeRange.start }, L"Multiple lines script should be contained in a CDATA section.")); - } - goto EVENT_SUCCESS; - } - else if (Ptr text = element->subNodes[0].Cast()) - { - auto value = MakePtr(); - value->binding = GlobalStringKey::Get(name->binding); - value->value = text->content.value; - value->attPosition = { {resource},element->codeRange.start }; - value->valuePosition = { {resource},text->content.codeRange.start }; - value->valuePosition.column += 9; // name), value); - } - goto EVENT_SUCCESS; - } - errors.Add(GuiResourceError({ {resource},element->codeRange.start }, L"Event script should be contained in a text or CDATA section.")); - EVENT_SUCCESS:; - } - } - } - } - } - } - - void GuiInstanceContext::FillAttSetter(Ptr resource, Ptr setter, Ptr xml, GuiResourceError::List& errors) - { - if (auto parser = GetParserManager()->GetParser(L"INSTANCE-ELEMENT-NAME")) - { - setter->tagPosition = { {resource},xml->codeRange.start }; - - // collect attributes as setters - FOREACH(Ptr, att, xml->attributes) - { - if(auto name = parser->Parse({ resource }, att->name.value, att->name.codeRange.start, errors)) - { - if (name->IsReferenceAttributeName()) - { - // collect reference attributes - if (name->name == L"Name") - { - setter->instanceName = GlobalStringKey::Get(att->value.value); - } - } - else if (name->IsEnvironmentAttributeName()) - { - // collect environment variables - if (setter->environmentVariables.Keys().Contains(GlobalStringKey::Get(name->name))) - { - errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Duplicated environment variable \"" + name->name + L"\".")); - } - else - { - auto value = MakePtr(); - value->value = att->value.value; - value->attPosition = { {resource},att->codeRange.start }; - value->valuePosition = { {resource},att->value.codeRange.start }; - value->valuePosition.column += 1; - setter->environmentVariables.Add(GlobalStringKey::Get(name->name), value); - } - } - else if (name->IsPropertyAttributeName()) - { - // collect attributes setters - if (setter->setters.Keys().Contains(GlobalStringKey::Get(name->name))) - { - errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Duplicated property \"" + name->name + L"\".")); - } - else - { - auto sv = MakePtr(); - sv->binding = GlobalStringKey::Get(name->binding); - sv->attPosition = { {resource},att->codeRange.start }; - setter->setters.Add(GlobalStringKey::Get(name->name), sv); - - Ptr value = new GuiTextRepr; - value->text = att->value.value; - value->tagPosition = { {resource},att->value.codeRange.start }; - value->tagPosition.column += 1; - sv->values.Add(value); - } - } - else if (name->IsEventAttributeName()) - { - // collect event setters - if (setter->eventHandlers.Keys().Contains(GlobalStringKey::Get(name->name))) - { - errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Duplicated event \"" + name->name + L"\".")); - } - else - { - auto value = MakePtr(); - value->binding = GlobalStringKey::Get(name->binding); - value->value = att->value.value; - value->attPosition = { {resource},att->codeRange.start }; - value->valuePosition = { {resource},att->value.codeRange.start }; - value->valuePosition.column += 1; - setter->eventHandlers.Add(GlobalStringKey::Get(name->name), value); - } - } - else - { - errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Unknown attribute name: \"" + att->name.value + L"\".")); - } - } - } - - // collect attributes and events - CollectAttributes(resource, setter->setters, xml, errors); - CollectEvents(resource, setter->eventHandlers, xml, errors); - } - } - - Ptr GuiInstanceContext::LoadCtor(Ptr resource, Ptr xml, GuiResourceError::List& errors) - { - if (auto parser = GetParserManager()->GetParser(L"INSTANCE-ELEMENT-NAME")) - { - if(auto ctorName = parser->Parse({ resource }, xml->name.value, xml->name.codeRange.start, errors)) - { - if (ctorName->IsCtorName()) - { - Ptr ctor = new GuiConstructorRepr; - ctor->typeNamespace = GlobalStringKey::Get(ctorName->namespaceName); - ctor->typeName = GlobalStringKey::Get(ctorName->name); - // collect attributes as setters - FOREACH(Ptr, att, xml->attributes) - { - if(auto attName = parser->Parse({ resource }, att->name.value, att->name.codeRange.start, errors)) - { - if (attName->IsReferenceAttributeName()) - { - if (attName->name == L"Style") - { - ctor->styleName = att->value.value; - } - } - } - } - FillAttSetter(resource, ctor, xml, errors); - return ctor; - } - else - { - errors.Add(GuiResourceError({ {resource},xml->codeRange.start }, L"Wrong constructor name \"" + xml->name.value + L"\".")); - } - } - } - return 0; - } - - Ptr GuiInstanceContext::LoadFromXml(Ptr resource, Ptr xml, GuiResourceError::List& errors) - { - Ptr context = new GuiInstanceContext; - context->tagPosition = { {resource},xml->rootElement->codeRange.start }; - - if (xml->rootElement->name.value == L"Instance") - { - if (auto codeBehindAttr = XmlGetAttribute(xml->rootElement, L"ref.CodeBehind")) - { - context->codeBehind = codeBehindAttr->value.value == L"true"; - } - - // load type name - if (auto classAttr = XmlGetAttribute(xml->rootElement, L"ref.Class")) - { - context->className = classAttr->value.value; - context->classPosition = { {resource},classAttr->codeRange.start }; - } - - // load style names - if (auto styleAttr = XmlGetAttribute(xml->rootElement, L"ref.Styles")) - { - SplitBySemicolon(styleAttr->value.value, context->stylePaths); - context->stylePosition = { {resource},styleAttr->codeRange.start }; - } - - // load namespaces - List> namespaceAttributes; - CopyFrom(namespaceAttributes, xml->rootElement->attributes); - if (!XmlGetAttribute(xml->rootElement, L"xmlns")) - { - Ptr att = new XmlAttribute; - att->name.value = L"xmlns"; - att->value.value = - L"presentation::controls::Gui*;" - L"presentation::elements::Gui*Element;" - L"presentation::compositions::Gui*Composition;" - L"presentation::compositions::Gui*;" - L"presentation::templates::Gui*;" - L"system::*;" - L"system::reflection::*;" - L"presentation::*;" - L"presentation::Gui*;" - L"presentation::controls::*;" - L"presentation::controls::list::*;" - L"presentation::controls::tree::*;" - L"presentation::elements::*;" - L"presentation::elements::Gui*;" - L"presentation::elements::text::*;" - L"presentation::compositions::*;" - L"presentation::templates::*;" - L"presentation::theme::*"; - namespaceAttributes.Add(att); - } - FOREACH(Ptr, att, namespaceAttributes) - { - // check if the attribute defines a namespace - WString attName = att->name.value; - if (attName.Length() >= 5 && attName.Left(5) == L"xmlns") - { - GlobalStringKey ns; - if (attName.Length() > 6) - { - if (attName.Left(6) == L"xmlns:") - { - ns = GlobalStringKey::Get(attName.Sub(6, attName.Length() - 6)); - } - else - { - continue; - } - } - - // create a data structure for the namespace - Ptr info; - vint index = context->namespaces.Keys().IndexOf(ns); - if (index == -1) - { - info = new NamespaceInfo; - info->name = ns; - info->attPosition = { {resource},att->codeRange.start }; - context->namespaces.Add(ns, info); - } - else - { - info = context->namespaces.Values()[index]; - } - - // extract all patterns in the namespace, split the value by ';' - List patterns; - SplitBySemicolon(att->value.value, patterns); - FOREACH(WString, pattern, patterns) - { - // add the pattern to the namespace - Ptr ns = new GuiInstanceNamespace; - Pair star = INVLOC.FindFirst(pattern, L"*", Locale::None); - if (star.key == -1) - { - ns->prefix = pattern; - } - else - { - ns->prefix = pattern.Sub(0, star.key); - ns->postfix = pattern.Sub(star.key + star.value, pattern.Length() - star.key - star.value); - } - info->namespaces.Add(ns); - } - } - } - - // load instance - FOREACH(Ptr, element, XmlGetElements(xml->rootElement)) - { - if (element->name.value == L"ref.Parameter") - { - auto attName = XmlGetAttribute(element, L"Name"); - auto attClass = XmlGetAttribute(element, L"Class"); - if (attName && attClass) - { - auto parameter = MakePtr(); - parameter->name = GlobalStringKey::Get(attName->value.value); - parameter->className = GlobalStringKey::Get(attClass->value.value); - parameter->tagPosition = { {resource},element->codeRange.start }; - parameter->classPosition = { {resource},attClass->value.codeRange.start }; - parameter->classPosition.column += 1; - context->parameters.Add(parameter); - } - else - { - errors.Add(GuiResourceError({ {resource},element->codeRange.start }, L"ref.Parameter requires the following attributes existing at the same time: Name, Class.")); - } - } - -#define COLLECT_SCRIPT(NAME, SCRIPT, POSITION)\ - (element->name.value == L"ref." #NAME)\ - {\ - if (element->subNodes.Count() == 1)\ - {\ - if (auto cdata = element->subNodes[0].Cast())\ - {\ - context->SCRIPT = cdata->content.value;\ - context->POSITION = { {resource},cdata->codeRange.start };\ - context->POSITION.column += 9; /* codeRange.start }, L"Script should be contained in a CDATA section."));\ - NAME##_SCRIPT_SUCCESS:;\ - }\ - - else if COLLECT_SCRIPT(Members, memberScript, memberPosition) - else if COLLECT_SCRIPT(Ctor, ctorScript, ctorPosition) - else if COLLECT_SCRIPT(Dtor, dtorScript, dtorPosition) - -#undef COLLECT_SCRIPT - else if (!context->instance) - { - context->instance = LoadCtor(resource, element, errors); - } - } - } - else - { - errors.Add(GuiResourceError({ {resource},xml->rootElement->codeRange.start }, L"The root element of instance should be \"Instance\".")); - } - - return context->instance ? context : nullptr; - } - - Ptr GuiInstanceContext::SaveToXml() - { - auto xmlInstance = MakePtr(); - xmlInstance->name.value = L"Instance"; - - { - auto attCodeBehind = MakePtr(); - attCodeBehind->name.value = L"ref.CodeBehind"; - attCodeBehind->value.value = codeBehind ? L"true" : L"false"; - xmlInstance->attributes.Add(attCodeBehind); - } - - auto attClass = MakePtr(); - attClass->name.value = L"ref.Class"; - attClass->value.value = className; - xmlInstance->attributes.Add(attClass); - - for (vint i = 0; i < namespaces.Count(); i++) - { - auto key = namespaces.Keys()[i]; - auto value = namespaces.Values()[i]; - - auto xmlns = MakePtr(); - xmlns->name.value = L"xmlns"; - if (key != GlobalStringKey::Empty) - { - xmlns->name.value += L":" + key.ToString(); - } - xmlInstance->attributes.Add(xmlns); - - for (vint j = 0; j < value->namespaces.Count(); j++) - { - auto ns = value->namespaces[j]; - if (j != 0) - { - xmlns->value.value += L";"; - } - xmlns->value.value += ns->prefix + L"*" + ns->postfix; - } - } - - FOREACH(Ptr, parameter, parameters) - { - auto xmlParameter = MakePtr(); - xmlParameter->name.value = L"ref.Parameter"; - xmlInstance->subNodes.Add(xmlParameter); - - auto attName = MakePtr(); - attName->name.value = L"Name"; - attName->value.value = parameter->name.ToString(); - xmlParameter->attributes.Add(attName); - - auto attClass = MakePtr(); - attClass->name.value = L"Class"; - attClass->value.value = parameter->className.ToString(); - xmlParameter->attributes.Add(attClass); - } - -#define SERIALIZE_SCRIPT(NAME, SCRIPT)\ - if (SCRIPT != L"")\ - {\ - auto xmlScript = MakePtr();\ - xmlScript->name.value = L"ref." #NAME;\ - xmlInstance->subNodes.Add(xmlScript);\ - auto text = MakePtr();\ - text->content.value = SCRIPT;\ - xmlScript->subNodes.Add(text);\ - }\ - - SERIALIZE_SCRIPT(Members, memberScript) - SERIALIZE_SCRIPT(Ctpr, ctorScript) - SERIALIZE_SCRIPT(Dtor, dtorScript) - -#undef SERIALIZE_SCRIPT - - if (stylePaths.Count() > 0) - { - auto attStyles = MakePtr(); - attStyles->name.value = L"ref.Styles"; - xmlInstance->attributes.Add(attStyles); - - for (vint j = 0; j < stylePaths.Count(); j++) - { - if (j != 0) - { - attStyles->value.value += L";"; - } - attStyles->value.value += stylePaths[j]; - } - } - - instance->FillXml(xmlInstance); - - auto doc = MakePtr(); - doc->rootElement = xmlInstance; - return doc; - } - - bool GuiInstanceContext::ApplyStyles(Ptr resource, Ptr resolver, GuiResourceError::List& errors) - { - if (!appliedStyles) - { - appliedStyles = true; - - List> styles; - FOREACH(WString, uri, stylePaths) - { - WString protocol, path; - if (IsResourceUrl(uri, protocol, path)) - { - if (auto styleContext = resolver->ResolveResource(protocol, path).Cast()) - { - CopyFrom(styles, styleContext->styles, true); - } - else - { - errors.Add(GuiResourceError({ resource }, stylePosition, L"Failed to find the style referred in attribute \"ref.Styles\": \"" + uri + L"\".")); - } - } - else - { - errors.Add(GuiResourceError({ resource }, stylePosition, L"Invalid path in attribute \"ref.Styles\": \"" + uri + L"\".")); - } - } - - FOREACH(Ptr, style, styles) - { - List> output; - ExecuteQuery(style->query, this, output); - FOREACH(Ptr, ctor, output) - { - ApplyStyle(style, ctor); - } - } - - return true; - } - else - { - return false; - } - } - -/*********************************************************************** -GuiInstanceStyle -***********************************************************************/ - - namespace visitors - { - class SetStyleMarkVisitor : public Object, public GuiValueRepr::IVisitor - { - public: - void Visit(GuiTextRepr* repr)override - { - repr->fromStyle = true; - } - - void Visit(GuiAttSetterRepr* repr)override - { - repr->fromStyle = true; - FOREACH(Ptr, value, repr->setters.Values()) - { - FOREACH(Ptr, subValue, value->values) - { - subValue->Accept(this); - } - } - FOREACH(Ptr, value, repr->eventHandlers.Values()) - { - value->fromStyle = true; - } - FOREACH(Ptr, value, repr->environmentVariables.Values()) - { - value->fromStyle = true; - } - } - - void Visit(GuiConstructorRepr* repr)override - { - Visit((GuiAttSetterRepr*)repr); - } - }; - } - using namespace visitors; - - Ptr GuiInstanceStyle::LoadFromXml(Ptr resource, Ptr xml, GuiResourceError::List& errors) - { - auto style = MakePtr(); - if (auto pathAttr = XmlGetAttribute(xml, L"ref.Path")) - { - auto position = pathAttr->value.codeRange.start; - position.column += 1; - - auto parser = GetParserManager()->GetParser(L"INSTANCE-QUERY"); - auto query = parser->Parse({ resource }, pathAttr->value.value, position, errors); - if (!query) return nullptr; - style->query = query; - } - else - { - errors.Add(GuiResourceError({ {resource},xml->codeRange.start }, L"Missing attribute \"ref.Path\" in