From ba029cae8336b14246c5d133ffda123ccbd5cc36 Mon Sep 17 00:00:00 2001 From: vczh Date: Fri, 2 May 2025 22:29:10 -0700 Subject: [PATCH] Update release --- Import/GacUI.UnitTest.UI.cpp | 308 +- Import/GacUI.UnitTest.UI.h | 67 +- Import/GacUI.UnitTest.UIReflection.cpp | 5 - Import/GacUI.UnitTest.UIReflection.h | 4 - Import/GacUI.UnitTest.cpp | 321 +- Import/GacUI.UnitTest.h | 428 ++- Import/GacUI.Windows.cpp | 91 +- Import/GacUI.Windows.h | 4 +- Import/GacUI.cpp | 3284 ++++++++++++++++- Import/GacUI.h | 1771 ++++++--- Import/Metadata/RemoteProtocol.json | 199 +- .../RemoteProtocol/Protocol_Renderer.txt | 1 + .../RemoteProtocol/Protocol_SyncDom.txt | 63 +- .../RemoteProtocol/Protocol_UnitTest.txt | 29 + Import/Metadata/RemoteProtocol/Protocols.txt | 3 +- Import/VlppOS.cpp | 1 + Tools/Reflection32.bin | Bin 972028 -> 971330 bytes Tools/Reflection64.bin | Bin 972028 -> 971330 bytes Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x64 | Bin 25176 -> 25176 bytes Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x86 | Bin 25176 -> 25176 bytes 20 files changed, 5320 insertions(+), 1259 deletions(-) create mode 100644 Import/Metadata/RemoteProtocol/Protocol_UnitTest.txt diff --git a/Import/GacUI.UnitTest.UI.cpp b/Import/GacUI.UnitTest.UI.cpp index 916f07dc..736f80b2 100644 --- a/Import/GacUI.UnitTest.UI.cpp +++ b/Import/GacUI.UnitTest.UI.cpp @@ -48,8 +48,8 @@ Helper Functions } void InstallDom( - const remoteprotocol::RenderingTrace& trace, - const remoteprotocol::RenderingFrame& frame, + const remoteprotocol::UnitTest_RenderingTrace& trace, + const remoteprotocol::UnitTest_RenderingFrame& frame, GuiGraphicsComposition* container, vint x, vint y, @@ -59,17 +59,17 @@ Helper Functions auto bounds = new GuiBoundsComposition; container->AddChild(bounds); - bounds->SetExpectedBounds(Rect({ dom->bounds.x1 - x,dom->bounds.y1 - y }, { dom->bounds.Width(),dom->bounds.Height() })); + bounds->SetExpectedBounds(Rect({ dom->content.bounds.x1 - x,dom->content.bounds.y1 - y }, { dom->content.bounds.Width(),dom->content.bounds.Height() })); - auto cursor = dom->cursor; - if (dom->cursor) + auto cursor = dom->content.cursor; + if (dom->content.cursor) { cursorCounter++; } - if (dom->hitTestResult && cursorCounter == 0) + if (dom->content.hitTestResult && cursorCounter == 0) { - switch (dom->hitTestResult.Value()) + switch (dom->content.hitTestResult.Value()) { case INativeWindowListener::BorderLeft: case INativeWindowListener::BorderRight: @@ -104,9 +104,9 @@ Helper Functions bounds->SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(cursor.Value())); } - if (dom->element) + if (dom->content.element) { - switch (trace.createdElements->Get(dom->element.Value())) + switch (trace.createdElements->Get(dom->content.element.Value())) { case remoteprotocol::RendererType::FocusRectangle: { @@ -118,7 +118,7 @@ Helper Functions { auto element = Ptr(GuiSolidBorderElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetColor(desc.borderColor); element->SetShape(desc.shape); @@ -128,7 +128,7 @@ Helper Functions { auto element = Ptr(Gui3DBorderElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetColors(desc.leftTopColor, desc.rightBottomColor); } @@ -137,7 +137,7 @@ Helper Functions { auto element = Ptr(Gui3DSplitterElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetColors(desc.leftTopColor, desc.rightBottomColor); element->SetDirection(desc.direction); @@ -147,7 +147,7 @@ Helper Functions { auto element = Ptr(GuiSolidBackgroundElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetColor(desc.backgroundColor); element->SetShape(desc.shape); @@ -157,7 +157,7 @@ Helper Functions { auto element = Ptr(GuiGradientBackgroundElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetColors(desc.leftTopColor, desc.rightBottomColor); element->SetDirection(desc.direction); @@ -168,7 +168,7 @@ Helper Functions { auto element = Ptr(GuiInnerShadowElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetColor(desc.shadowColor); element->SetThickness(desc.thickness); @@ -178,7 +178,7 @@ Helper Functions { auto element = Ptr(GuiSolidLabelElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetColor(desc.textColor); element->SetAlignments(GetAlignment(desc.horizontalAlignment), GetAlignment(desc.verticalAlignment)); @@ -194,7 +194,7 @@ Helper Functions { auto element = Ptr(GuiPolygonElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetSize(desc.size); element->SetBorderColor(desc.borderColor); @@ -210,7 +210,7 @@ Helper Functions { auto element = Ptr(GuiImageFrameElement::Create()); bounds->SetOwnedElement(element); - auto& desc = frame.elements->Get(dom->element.Value()).Get(); + auto& desc = frame.elements->Get(dom->content.element.Value()).Get(); element->SetAlignments(GetAlignment(desc.horizontalAlignment), GetAlignment(desc.verticalAlignment)); element->SetStretch(desc.stretch); @@ -238,17 +238,17 @@ Helper Functions { for (auto child : *dom->children.Obj()) { - InstallDom(trace, frame, bounds, dom->bounds.x1, dom->bounds.y1, child, cursorCounter); + InstallDom(trace, frame, bounds, dom->content.bounds.x1, dom->content.bounds.y1, child, cursorCounter); } } - if (dom->cursor) + if (dom->content.cursor) { cursorCounter--; } } - GuiBoundsComposition* BuildRootComposition(const remoteprotocol::RenderingTrace& trace, const remoteprotocol::RenderingFrame& frame) + GuiBoundsComposition* BuildRootComposition(const remoteprotocol::UnitTest_RenderingTrace& trace, const remoteprotocol::UnitTest_RenderingFrame& frame) { vint w = frame.windowSize.clientBounds.Width().value; vint h = frame.windowSize.clientBounds.Height().value; @@ -373,24 +373,6 @@ Closures //------------------------------------------------------------------- - __vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_::__vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0) - :__vwsnthis_0(::vl::__vwsn::This(__vwsnctorthis_0)) - { - } - - void __vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_::operator()(const ::vl::reflection::description::Value& __vwsn_value_) const - { - auto __vwsn_old_ = ::vl::__vwsn::This(__vwsnthis_0->self)->GetStrings(); - auto __vwsn_new_ = ::vl::__vwsn::Unbox<::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerStringsStrings>>(__vwsn_value_); - if ((__vwsn_old_.Obj() == __vwsn_new_.Obj())) - { - return; - } - ::vl::__vwsn::This(__vwsnthis_0->self)->SetStrings(__vwsn_new_); - } - - //------------------------------------------------------------------- - __vwsnf1_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_::__vwsnf1_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0) :__vwsnthis_0(::vl::__vwsn::This(__vwsnctorthis_0)) { @@ -491,13 +473,13 @@ Closures void __vwsnf8_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_::operator()(const ::vl::reflection::description::Value& __vwsn_value_) const { - auto __vwsn_old_ = ::vl::__vwsn::This(__vwsnthis_0->__vwsn_precompile_22)->GetText(); + auto __vwsn_old_ = ::vl::__vwsn::This(__vwsnthis_0->self)->GetText(); auto __vwsn_new_ = ::vl::__vwsn::Unbox<::vl::WString>(__vwsn_value_); if ((__vwsn_old_ == __vwsn_new_)) { return; } - ::vl::__vwsn::This(__vwsnthis_0->__vwsn_precompile_22)->SetText(__vwsn_new_); + ::vl::__vwsn::This(__vwsnthis_0->self)->SetText(__vwsn_new_); } //------------------------------------------------------------------- @@ -509,13 +491,13 @@ Closures void __vwsnf9_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_::operator()(const ::vl::reflection::description::Value& __vwsn_value_) const { - auto __vwsn_old_ = ::vl::__vwsn::This(__vwsnthis_0->self)->GetText(); - auto __vwsn_new_ = ::vl::__vwsn::Unbox<::vl::WString>(__vwsn_value_); - if ((__vwsn_old_ == __vwsn_new_)) + auto __vwsn_old_ = ::vl::__vwsn::This(__vwsnthis_0->self)->GetStrings(); + auto __vwsn_new_ = ::vl::__vwsn::Unbox<::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerStringsStrings>>(__vwsn_value_); + if ((__vwsn_old_.Obj() == __vwsn_new_.Obj())) { return; } - ::vl::__vwsn::This(__vwsnthis_0->self)->SetText(__vwsn_new_); + ::vl::__vwsn::This(__vwsnthis_0->self)->SetStrings(__vwsn_new_); } //------------------------------------------------------------------- @@ -665,7 +647,7 @@ Closures void __vwsnc3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_activator_() { - auto __vwsn_bind_activator_result_ = [&](){ try{ return ::vl::__vwsn::This(::vl::__vwsn::Unbox<::vl::Ptr<::gaclib_controls::IUnitTestSnapshotFrame>>(::vl::__vwsn::This(__vwsn_bind_cache_0)->GetSelectedItem()).Obj())->GetCommandsAsJsonText(); } catch(...){ return ::vl::WString::Unmanaged(L""); } }(); + auto __vwsn_bind_activator_result_ = [&](){ try{ return ::vl::__vwsn::This(::vl::__vwsn::Unbox<::vl::Ptr<::gaclib_controls::IUnitTestSnapshotFrame>>(::vl::__vwsn::This(__vwsn_bind_cache_0)->GetSelectedItem()).Obj())->GetElementsAsJsonText(); } catch(...){ return ::vl::WString::Unmanaged(L""); } }(); ::vl::__vwsn::EventInvoke(this->ValueChanged)(::vl::__vwsn::Box(__vwsn_bind_activator_result_)); } @@ -715,10 +697,11 @@ Closures //------------------------------------------------------------------- - __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0) - :__vwsnthis_0(::vl::__vwsn::This(__vwsnctorthis_0)) + __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindow* __vwsnctor___vwsn_this_, ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0) + :__vwsn_this_(__vwsnctor___vwsn_this_) + , __vwsnthis_0(::vl::__vwsn::This(__vwsnctorthis_0)) { - this->__vwsn_bind_cache_0 = static_cast<::vl::presentation::controls::GuiBindableTextList*>(nullptr); + this->__vwsn_bind_cache_0 = static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr); this->__vwsn_bind_handler_0_0 = ::vl::Ptr<::vl::reflection::description::IEventHandler>(); this->__vwsn_bind_opened_ = false; this->__vwsn_bind_closed_ = false; @@ -726,11 +709,11 @@ Closures void __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_activator_() { - auto __vwsn_bind_activator_result_ = [&](){ try{ return ::vl::__vwsn::This(::vl::__vwsn::Unbox<::vl::Ptr<::gaclib_controls::IUnitTestSnapshotFrame>>(::vl::__vwsn::This(__vwsn_bind_cache_0)->GetSelectedItem()).Obj())->GetElementsAsJsonText(); } catch(...){ return ::vl::WString::Unmanaged(L""); } }(); + auto __vwsn_bind_activator_result_ = ::vl::__vwsn::This(::vl::__vwsn::This(__vwsn_bind_cache_0)->GetStrings().Obj())->WindowTitle(); ::vl::__vwsn::EventInvoke(this->ValueChanged)(::vl::__vwsn::Box(__vwsn_bind_activator_result_)); } - void __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0(::vl::presentation::compositions::GuiGraphicsComposition* __vwsn_bind_callback_argument_0, ::vl::presentation::compositions::GuiEventArgs* __vwsn_bind_callback_argument_1) + void __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0() { this->__vwsn_bind_activator_(); } @@ -740,8 +723,8 @@ Closures if ((! __vwsn_bind_opened_)) { (__vwsn_bind_opened_ = true); - (__vwsn_bind_cache_0 = [&](){ try{ return __vwsnthis_0->textListFrames; } catch(...){ return static_cast<::vl::presentation::controls::GuiBindableTextList*>(nullptr); } }()); - (__vwsn_bind_handler_0_0 = [&](){ try{ return ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_bind_cache_0)->SelectionChanged, ::vl::Func(this, &__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0)); } catch(...){ return ::vl::Ptr<::vl::reflection::description::IEventHandler>(); } }()); + (__vwsn_bind_cache_0 = [&](){ try{ return __vwsn_this_; } catch(...){ return static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr); } }()); + (__vwsn_bind_handler_0_0 = [&](){ try{ return ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_bind_cache_0)->StringsChanged, ::vl::Func(this, &__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0)); } catch(...){ return ::vl::Ptr<::vl::reflection::description::IEventHandler>(); } }()); return true; } return false; @@ -764,10 +747,10 @@ Closures (__vwsn_bind_closed_ = true); if (static_cast(__vwsn_bind_handler_0_0)) { - ::vl::__vwsn::EventDetach(::vl::__vwsn::This(__vwsn_bind_cache_0)->SelectionChanged, __vwsn_bind_handler_0_0); + ::vl::__vwsn::EventDetach(::vl::__vwsn::This(__vwsn_bind_cache_0)->StringsChanged, __vwsn_bind_handler_0_0); (__vwsn_bind_handler_0_0 = ::vl::Ptr<::vl::reflection::description::IEventHandler>()); } - (__vwsn_bind_cache_0 = static_cast<::vl::presentation::controls::GuiBindableTextList*>(nullptr)); + (__vwsn_bind_cache_0 = static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr)); (__vwsn_bind_handler_0_0 = ::vl::Ptr<::vl::reflection::description::IEventHandler>()); return true; } @@ -776,11 +759,10 @@ Closures //------------------------------------------------------------------- - __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindow* __vwsnctor___vwsn_this_, ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0) - :__vwsn_this_(__vwsnctor___vwsn_this_) - , __vwsnthis_0(::vl::__vwsn::This(__vwsnctorthis_0)) + __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0) + :__vwsnthis_0(::vl::__vwsn::This(__vwsnctorthis_0)) { - this->__vwsn_bind_cache_0 = static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr); + this->__vwsn_bind_cache_0 = static_cast<::vl::presentation::controls::GuiApplication*>(nullptr); this->__vwsn_bind_handler_0_0 = ::vl::Ptr<::vl::reflection::description::IEventHandler>(); this->__vwsn_bind_opened_ = false; this->__vwsn_bind_closed_ = false; @@ -788,7 +770,7 @@ Closures void __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_activator_() { - auto __vwsn_bind_activator_result_ = ::vl::__vwsn::This(::vl::__vwsn::This(__vwsn_bind_cache_0)->GetStrings().Obj())->WindowTitle(); + auto __vwsn_bind_activator_result_ = ::gaclib_controls::UnitTestSnapshotViewerStrings::Get(::vl::__vwsn::This(__vwsn_bind_cache_0)->GetLocale()); ::vl::__vwsn::EventInvoke(this->ValueChanged)(::vl::__vwsn::Box(__vwsn_bind_activator_result_)); } @@ -802,8 +784,8 @@ Closures if ((! __vwsn_bind_opened_)) { (__vwsn_bind_opened_ = true); - (__vwsn_bind_cache_0 = [&](){ try{ return __vwsn_this_; } catch(...){ return static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr); } }()); - (__vwsn_bind_handler_0_0 = [&](){ try{ return ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_bind_cache_0)->StringsChanged, ::vl::Func(this, &__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0)); } catch(...){ return ::vl::Ptr<::vl::reflection::description::IEventHandler>(); } }()); + (__vwsn_bind_cache_0 = [&](){ try{ return ::vl::presentation::controls::GetApplication(); } catch(...){ return static_cast<::vl::presentation::controls::GuiApplication*>(nullptr); } }()); + (__vwsn_bind_handler_0_0 = [&](){ try{ return ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_bind_cache_0)->LocaleChanged, ::vl::Func(this, &__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0)); } catch(...){ return ::vl::Ptr<::vl::reflection::description::IEventHandler>(); } }()); return true; } return false; @@ -820,67 +802,6 @@ Closures } bool __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::Close() - { - if ((! __vwsn_bind_closed_)) - { - (__vwsn_bind_closed_ = true); - if (static_cast(__vwsn_bind_handler_0_0)) - { - ::vl::__vwsn::EventDetach(::vl::__vwsn::This(__vwsn_bind_cache_0)->StringsChanged, __vwsn_bind_handler_0_0); - (__vwsn_bind_handler_0_0 = ::vl::Ptr<::vl::reflection::description::IEventHandler>()); - } - (__vwsn_bind_cache_0 = static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr)); - (__vwsn_bind_handler_0_0 = ::vl::Ptr<::vl::reflection::description::IEventHandler>()); - return true; - } - return false; - } - - //------------------------------------------------------------------- - - __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0) - :__vwsnthis_0(::vl::__vwsn::This(__vwsnctorthis_0)) - { - this->__vwsn_bind_cache_0 = static_cast<::vl::presentation::controls::GuiApplication*>(nullptr); - this->__vwsn_bind_handler_0_0 = ::vl::Ptr<::vl::reflection::description::IEventHandler>(); - this->__vwsn_bind_opened_ = false; - this->__vwsn_bind_closed_ = false; - } - - void __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_activator_() - { - auto __vwsn_bind_activator_result_ = ::gaclib_controls::UnitTestSnapshotViewerStrings::Get(::vl::__vwsn::This(__vwsn_bind_cache_0)->GetLocale()); - ::vl::__vwsn::EventInvoke(this->ValueChanged)(::vl::__vwsn::Box(__vwsn_bind_activator_result_)); - } - - void __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0() - { - this->__vwsn_bind_activator_(); - } - - bool __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::Open() - { - if ((! __vwsn_bind_opened_)) - { - (__vwsn_bind_opened_ = true); - (__vwsn_bind_cache_0 = [&](){ try{ return ::vl::presentation::controls::GetApplication(); } catch(...){ return static_cast<::vl::presentation::controls::GuiApplication*>(nullptr); } }()); - (__vwsn_bind_handler_0_0 = [&](){ try{ return ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_bind_cache_0)->LocaleChanged, ::vl::Func(this, &__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::__vwsn_bind_callback_0_0)); } catch(...){ return ::vl::Ptr<::vl::reflection::description::IEventHandler>(); } }()); - return true; - } - return false; - } - - bool __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::Update() - { - if ((__vwsn_bind_opened_ && (! __vwsn_bind_closed_))) - { - this->__vwsn_bind_activator_(); - return true; - } - return false; - } - - bool __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription::Close() { if ((! __vwsn_bind_closed_)) { @@ -899,11 +820,11 @@ Closures //------------------------------------------------------------------- - __vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings::__vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings() + __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings::__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings() { } - ::vl::WString __vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings::WindowTitle() + ::vl::WString __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings::WindowTitle() { return ::vl::WString::Unmanaged(L"Unit Test Snapshot Viewer"); } @@ -934,7 +855,7 @@ Class (::gaclib_controls::UnitTestSnapshotViewerStrings) ::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerStringsStrings> UnitTestSnapshotViewerStrings::__vwsn_ls_en_US_BuildStrings(::vl::Locale __vwsn_ls_locale) { - return ::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerStringsStrings>(new ::vl_workflow_global::__vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings()); + return ::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerStringsStrings>(new ::vl_workflow_global::__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings()); } void UnitTestSnapshotViewerStrings::Install(::vl::Locale __vwsn_ls_locale, ::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerStringsStrings> __vwsn_ls_impl) @@ -971,15 +892,15 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) ::vl::__vwsn::This(this->self)->SetClientSize([&](){ ::vl::presentation::Size __vwsn_temp__; __vwsn_temp__.x = static_cast<::vl::vint>(1920); __vwsn_temp__.y = static_cast<::vl::vint>(1080); return __vwsn_temp__; }()); } (this->__vwsn_precompile_0 = new ::vl::presentation::compositions::GuiTableComposition()); + { + ::vl::__vwsn::This(this->__vwsn_precompile_0)->SetMinSizeLimitation(::vl::presentation::compositions::GuiGraphicsComposition::MinSizeLimitation::LimitToElementAndChildren); + } { ::vl::__vwsn::This(this->__vwsn_precompile_0)->SetCellPadding(static_cast<::vl::vint>(5)); } { ::vl::__vwsn::This(this->__vwsn_precompile_0)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(0); __vwsn_temp__.top = static_cast<::vl::vint>(0); __vwsn_temp__.right = static_cast<::vl::vint>(0); __vwsn_temp__.bottom = static_cast<::vl::vint>(0); return __vwsn_temp__; }()); } - { - ::vl::__vwsn::This(this->__vwsn_precompile_0)->SetMinSizeLimitation(::vl::presentation::compositions::GuiGraphicsComposition::MinSizeLimitation::LimitToElementAndChildren); - } { ::vl::__vwsn::This(this->__vwsn_precompile_0)->SetRowsAndColumns(static_cast<::vl::vint>(1), static_cast<::vl::vint>(4)); ::vl::__vwsn::This(this->__vwsn_precompile_0)->SetRowOption(static_cast<::vl::vint>(0), [&](){ ::vl::presentation::compositions::GuiCellOption __vwsn_temp__; __vwsn_temp__.composeType = ::vl::presentation::compositions::GuiCellOption::ComposeType::Percentage; __vwsn_temp__.percentage = static_cast(1.0); return __vwsn_temp__; }()); @@ -1013,12 +934,6 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) { ::vl::__vwsn::This(this->treeViewFileNodes)->SetChildrenProperty(vl::Func(::vl_workflow_global::__vwsnf1_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(this))); } - { - ::vl::__vwsn::This(this->treeViewFileNodes)->SetHorizontalAlwaysVisible(false); - } - { - ::vl::__vwsn::This(this->treeViewFileNodes)->SetVerticalAlwaysVisible(false); - } { ::vl::__vwsn::This(this->treeViewFileNodes)->SetTextProperty(vl::Func(::vl_workflow_global::__vwsnf2_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(this))); } @@ -1026,6 +941,12 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) { ::vl::__vwsn::This(this->__vwsn_precompile_4)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(0); __vwsn_temp__.top = static_cast<::vl::vint>(0); __vwsn_temp__.right = static_cast<::vl::vint>(0); __vwsn_temp__.bottom = static_cast<::vl::vint>(0); return __vwsn_temp__; }()); } + { + ::vl::__vwsn::This(this->treeViewFileNodes)->SetHorizontalAlwaysVisible(false); + } + { + ::vl::__vwsn::This(this->treeViewFileNodes)->SetVerticalAlwaysVisible(false); + } { ::vl::__vwsn::This(this->__vwsn_precompile_3)->AddChild(static_cast<::vl::presentation::compositions::GuiGraphicsComposition*>(::vl::__vwsn::This(this->treeViewFileNodes)->GetBoundsComposition())); } @@ -1049,12 +970,6 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) (this->textListFrames = new ::vl::presentation::controls::GuiBindableTextList(::vl::presentation::theme::ThemeName::TextList)); ::vl::__vwsn::This(__vwsn_this_)->SetNamedObject(::vl::WString::Unmanaged(L"textListFrames"), ::vl::__vwsn::Box(this->textListFrames)); } - { - ::vl::__vwsn::This(this->textListFrames)->SetHorizontalAlwaysVisible(false); - } - { - ::vl::__vwsn::This(this->textListFrames)->SetVerticalAlwaysVisible(false); - } { ::vl::__vwsn::This(this->textListFrames)->SetTextProperty(vl::Func(::vl_workflow_global::__vwsnf3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(this))); } @@ -1062,6 +977,12 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) { ::vl::__vwsn::This(this->__vwsn_precompile_9)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(0); __vwsn_temp__.top = static_cast<::vl::vint>(0); __vwsn_temp__.right = static_cast<::vl::vint>(0); __vwsn_temp__.bottom = static_cast<::vl::vint>(0); return __vwsn_temp__; }()); } + { + ::vl::__vwsn::This(this->textListFrames)->SetHorizontalAlwaysVisible(false); + } + { + ::vl::__vwsn::This(this->textListFrames)->SetVerticalAlwaysVisible(false); + } { ::vl::__vwsn::This(this->__vwsn_precompile_8)->AddChild(static_cast<::vl::presentation::controls::GuiControl*>(this->textListFrames)); } @@ -1096,16 +1017,16 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) (this->scRendering = new ::vl::presentation::controls::GuiScrollContainer(::vl::presentation::theme::ThemeName::ScrollView)); ::vl::__vwsn::This(__vwsn_this_)->SetNamedObject(::vl::WString::Unmanaged(L"scRendering"), ::vl::__vwsn::Box(this->scRendering)); } + (this->__vwsn_precompile_14 = ::vl::__vwsn::This(this->scRendering)->GetBoundsComposition()); + { + ::vl::__vwsn::This(this->__vwsn_precompile_14)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(5); __vwsn_temp__.top = static_cast<::vl::vint>(5); __vwsn_temp__.right = static_cast<::vl::vint>(5); __vwsn_temp__.bottom = static_cast<::vl::vint>(5); return __vwsn_temp__; }()); + } { ::vl::__vwsn::This(this->scRendering)->SetHorizontalAlwaysVisible(false); } { ::vl::__vwsn::This(this->scRendering)->SetVerticalAlwaysVisible(false); } - (this->__vwsn_precompile_14 = ::vl::__vwsn::This(this->scRendering)->GetBoundsComposition()); - { - ::vl::__vwsn::This(this->__vwsn_precompile_14)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(5); __vwsn_temp__.top = static_cast<::vl::vint>(5); __vwsn_temp__.right = static_cast<::vl::vint>(5); __vwsn_temp__.bottom = static_cast<::vl::vint>(5); return __vwsn_temp__; }()); - } { ::vl::__vwsn::This(this->__vwsn_precompile_13)->AddChild(static_cast<::vl::presentation::controls::GuiControl*>(this->scRendering)); } @@ -1122,16 +1043,16 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) { (this->__vwsn_precompile_16 = new ::vl::presentation::controls::GuiMultilineTextBox(::vl::presentation::theme::ThemeName::MultilineTextBox)); } + (this->__vwsn_precompile_17 = ::vl::__vwsn::This(this->__vwsn_precompile_16)->GetBoundsComposition()); + { + ::vl::__vwsn::This(this->__vwsn_precompile_17)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(5); __vwsn_temp__.top = static_cast<::vl::vint>(5); __vwsn_temp__.right = static_cast<::vl::vint>(5); __vwsn_temp__.bottom = static_cast<::vl::vint>(5); return __vwsn_temp__; }()); + } { ::vl::__vwsn::This(this->__vwsn_precompile_16)->SetHorizontalAlwaysVisible(false); } { ::vl::__vwsn::This(this->__vwsn_precompile_16)->SetVerticalAlwaysVisible(false); } - (this->__vwsn_precompile_17 = ::vl::__vwsn::This(this->__vwsn_precompile_16)->GetBoundsComposition()); - { - ::vl::__vwsn::This(this->__vwsn_precompile_17)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(5); __vwsn_temp__.top = static_cast<::vl::vint>(5); __vwsn_temp__.right = static_cast<::vl::vint>(5); __vwsn_temp__.bottom = static_cast<::vl::vint>(5); return __vwsn_temp__; }()); - } { ::vl::__vwsn::This(this->__vwsn_precompile_15)->AddChild(static_cast<::vl::presentation::controls::GuiControl*>(this->__vwsn_precompile_16)); } @@ -1143,21 +1064,21 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) (this->__vwsn_precompile_18 = new ::vl::presentation::controls::GuiTabPage(::vl::presentation::theme::ThemeName::CustomControl)); } { - ::vl::__vwsn::This(this->__vwsn_precompile_18)->SetText(::vl::WString::Unmanaged(L"Commands")); + ::vl::__vwsn::This(this->__vwsn_precompile_18)->SetText(::vl::WString::Unmanaged(L"Elements")); } { (this->__vwsn_precompile_19 = new ::vl::presentation::controls::GuiMultilineTextBox(::vl::presentation::theme::ThemeName::MultilineTextBox)); } + (this->__vwsn_precompile_20 = ::vl::__vwsn::This(this->__vwsn_precompile_19)->GetBoundsComposition()); + { + ::vl::__vwsn::This(this->__vwsn_precompile_20)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(5); __vwsn_temp__.top = static_cast<::vl::vint>(5); __vwsn_temp__.right = static_cast<::vl::vint>(5); __vwsn_temp__.bottom = static_cast<::vl::vint>(5); return __vwsn_temp__; }()); + } { ::vl::__vwsn::This(this->__vwsn_precompile_19)->SetHorizontalAlwaysVisible(false); } { ::vl::__vwsn::This(this->__vwsn_precompile_19)->SetVerticalAlwaysVisible(false); } - (this->__vwsn_precompile_20 = ::vl::__vwsn::This(this->__vwsn_precompile_19)->GetBoundsComposition()); - { - ::vl::__vwsn::This(this->__vwsn_precompile_20)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(5); __vwsn_temp__.top = static_cast<::vl::vint>(5); __vwsn_temp__.right = static_cast<::vl::vint>(5); __vwsn_temp__.bottom = static_cast<::vl::vint>(5); return __vwsn_temp__; }()); - } { ::vl::__vwsn::This(this->__vwsn_precompile_18)->AddChild(static_cast<::vl::presentation::controls::GuiControl*>(this->__vwsn_precompile_19)); } @@ -1165,32 +1086,6 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) auto __vwsn_collection_ = ::vl::__vwsn::UnboxCollection<::vl::reflection::description::IValueObservableList>(::vl::__vwsn::This(this->__vwsn_precompile_11)->GetPages()); ::vl::__vwsn::This(__vwsn_collection_.Obj())->Add(::vl::__vwsn::Box(this->__vwsn_precompile_18)); } - { - (this->__vwsn_precompile_21 = new ::vl::presentation::controls::GuiTabPage(::vl::presentation::theme::ThemeName::CustomControl)); - } - { - ::vl::__vwsn::This(this->__vwsn_precompile_21)->SetText(::vl::WString::Unmanaged(L"Elements")); - } - { - (this->__vwsn_precompile_22 = new ::vl::presentation::controls::GuiMultilineTextBox(::vl::presentation::theme::ThemeName::MultilineTextBox)); - } - { - ::vl::__vwsn::This(this->__vwsn_precompile_22)->SetHorizontalAlwaysVisible(false); - } - { - ::vl::__vwsn::This(this->__vwsn_precompile_22)->SetVerticalAlwaysVisible(false); - } - (this->__vwsn_precompile_23 = ::vl::__vwsn::This(this->__vwsn_precompile_22)->GetBoundsComposition()); - { - ::vl::__vwsn::This(this->__vwsn_precompile_23)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(5); __vwsn_temp__.top = static_cast<::vl::vint>(5); __vwsn_temp__.right = static_cast<::vl::vint>(5); __vwsn_temp__.bottom = static_cast<::vl::vint>(5); return __vwsn_temp__; }()); - } - { - ::vl::__vwsn::This(this->__vwsn_precompile_21)->AddChild(static_cast<::vl::presentation::controls::GuiControl*>(this->__vwsn_precompile_22)); - } - { - auto __vwsn_collection_ = ::vl::__vwsn::UnboxCollection<::vl::reflection::description::IValueObservableList>(::vl::__vwsn::This(this->__vwsn_precompile_11)->GetPages()); - ::vl::__vwsn::This(__vwsn_collection_.Obj())->Add(::vl::__vwsn::Box(this->__vwsn_precompile_21)); - } (this->__vwsn_precompile_12 = ::vl::__vwsn::This(this->__vwsn_precompile_11)->GetBoundsComposition()); { ::vl::__vwsn::This(this->__vwsn_precompile_12)->SetAlignmentToParent([&](){ ::vl::presentation::Margin __vwsn_temp__; __vwsn_temp__.left = static_cast<::vl::vint>(0); __vwsn_temp__.top = static_cast<::vl::vint>(0); __vwsn_temp__.right = static_cast<::vl::vint>(0); __vwsn_temp__.bottom = static_cast<::vl::vint>(0); return __vwsn_temp__; }()); @@ -1226,25 +1121,23 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) ::vl::__vwsn::This(__vwsn_this_)->AddSubscription(__vwsn_created_subscription_); } { - auto __vwsn_created_subscription_ = ::vl::Ptr<::vl::reflection::description::IValueSubscription>(new ::vl_workflow_global::__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(this)); + auto __vwsn_created_subscription_ = ::vl::Ptr<::vl::reflection::description::IValueSubscription>(new ::vl_workflow_global::__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(__vwsn_this_, this)); ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_created_subscription_.Obj())->ValueChanged, vl::Func(::vl_workflow_global::__vwsnf8_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(this))); ::vl::__vwsn::This(__vwsn_this_)->AddSubscription(__vwsn_created_subscription_); } { - auto __vwsn_created_subscription_ = ::vl::Ptr<::vl::reflection::description::IValueSubscription>(new ::vl_workflow_global::__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(__vwsn_this_, this)); + auto __vwsn_created_subscription_ = ::vl::Ptr<::vl::reflection::description::IValueSubscription>(new ::vl_workflow_global::__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(this)); ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_created_subscription_.Obj())->ValueChanged, vl::Func(::vl_workflow_global::__vwsnf9_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(this))); ::vl::__vwsn::This(__vwsn_this_)->AddSubscription(__vwsn_created_subscription_); } - { - auto __vwsn_created_subscription_ = ::vl::Ptr<::vl::reflection::description::IValueSubscription>(new ::vl_workflow_global::__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(this)); - ::vl::__vwsn::EventAttach(::vl::__vwsn::This(__vwsn_created_subscription_.Obj())->ValueChanged, vl::Func(::vl_workflow_global::__vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(this))); - ::vl::__vwsn::This(__vwsn_this_)->AddSubscription(__vwsn_created_subscription_); - } } UnitTestSnapshotViewerWindowConstructor::UnitTestSnapshotViewerWindowConstructor() - : self(static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr)) - , ViewModel(::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerViewModel>()) + : ViewModel(::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerViewModel>()) + , self(static_cast<::gaclib_controls::UnitTestSnapshotViewerWindow*>(nullptr)) + , treeViewFileNodes(static_cast<::vl::presentation::controls::GuiBindableTreeView*>(nullptr)) + , textListFrames(static_cast<::vl::presentation::controls::GuiBindableTextList*>(nullptr)) + , scRendering(static_cast<::vl::presentation::controls::GuiScrollContainer*>(nullptr)) , __vwsn_precompile_0(static_cast<::vl::presentation::compositions::GuiTableComposition*>(nullptr)) , __vwsn_precompile_1(static_cast<::vl::presentation::compositions::GuiColumnSplitterComposition*>(nullptr)) , __vwsn_precompile_2(static_cast<::vl::presentation::compositions::GuiColumnSplitterComposition*>(nullptr)) @@ -1266,12 +1159,6 @@ Class (::gaclib_controls::UnitTestSnapshotViewerWindowConstructor) , __vwsn_precompile_18(static_cast<::vl::presentation::controls::GuiTabPage*>(nullptr)) , __vwsn_precompile_19(static_cast<::vl::presentation::controls::GuiMultilineTextBox*>(nullptr)) , __vwsn_precompile_20(static_cast<::vl::presentation::compositions::GuiBoundsComposition*>(nullptr)) - , __vwsn_precompile_21(static_cast<::vl::presentation::controls::GuiTabPage*>(nullptr)) - , __vwsn_precompile_22(static_cast<::vl::presentation::controls::GuiMultilineTextBox*>(nullptr)) - , __vwsn_precompile_23(static_cast<::vl::presentation::compositions::GuiBoundsComposition*>(nullptr)) - , treeViewFileNodes(static_cast<::vl::presentation::controls::GuiBindableTreeView*>(nullptr)) - , textListFrames(static_cast<::vl::presentation::controls::GuiBindableTextList*>(nullptr)) - , scRendering(static_cast<::vl::presentation::controls::GuiScrollContainer*>(nullptr)) { } @@ -1445,20 +1332,20 @@ UnitTestSnapshotFrame class UnitTestSnapshotFrame : public Object, public virtual IUnitTestSnapshotFrame { protected: - vint index; - RenderingFrame frame; - WString elements; - WString commands; - WString dom; - JsonFormatting formatting; + vint index; + UnitTest_RenderingFrame frame; + WString elements; + WString commands; + WString dom; + JsonFormatting formatting; - friend const remoteprotocol::RenderingFrame& GetRenderingFrame(Ptr frame) + friend const remoteprotocol::UnitTest_RenderingFrame& GetRenderingFrame(Ptr frame) { return frame.Cast()->frame; } public: - UnitTestSnapshotFrame(vint _index, RenderingFrame _frame) + UnitTestSnapshotFrame(vint _index, UnitTest_RenderingFrame _frame) : index(_index) , frame(_frame) { @@ -1489,15 +1376,6 @@ UnitTestSnapshotFrame return elements; } - WString GetCommandsAsJsonText() override - { - if (commands == L"") - { - commands = JsonToString(ConvertCustomTypeToJson(frame.commands), formatting); - } - return commands; - } - WString GetDomAsJsonText() override { if (dom == L"") @@ -1516,10 +1394,10 @@ UnitTestSnapshotFileNode { protected: File file; - Ptr renderingTrace; + Ptr renderingTrace; List> frames; - friend const remoteprotocol::RenderingTrace& GetRenderingTrace(Ptr node) + friend const remoteprotocol::UnitTest_RenderingTrace& GetRenderingTrace(Ptr node) { return *node.Cast()->renderingTrace.Obj(); } @@ -1534,7 +1412,7 @@ UnitTestSnapshotFileNode glr::json::Parser parser; jsonNode = JsonParse(jsonText, parser); } - renderingTrace = Ptr(new RenderingTrace); + renderingTrace = Ptr(new UnitTest_RenderingTrace); ConvertJsonToCustomType(jsonNode, *renderingTrace.Obj()); frames.Clear(); diff --git a/Import/GacUI.UnitTest.UI.h b/Import/GacUI.UnitTest.UI.h index 6551dcba..b38ed8b6 100644 --- a/Import/GacUI.UnitTest.UI.h +++ b/Import/GacUI.UnitTest.UI.h @@ -38,7 +38,6 @@ https://github.com/vczh-libraries namespace vl_workflow_global { - struct __vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; struct __vwsnf1_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; struct __vwsnf2_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; struct __vwsnf3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; @@ -53,8 +52,7 @@ namespace vl_workflow_global class __vwsnc3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; class __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; class __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; - class __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; - class __vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings; + class __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings; } namespace __vwsn_enums @@ -101,7 +99,6 @@ namespace gaclib_controls public: virtual ::vl::WString GetName() = 0; virtual ::vl::WString GetElementsAsJsonText() = 0; - virtual ::vl::WString GetCommandsAsJsonText() = 0; virtual ::vl::WString GetDomAsJsonText() = 0; }; @@ -125,7 +122,7 @@ namespace gaclib_controls class UnitTestSnapshotViewerStrings : public ::vl::Object, public ::vl::reflection::Description { - friend class ::vl_workflow_global::__vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings; + friend class ::vl_workflow_global::__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings; #ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA friend struct ::vl::reflection::description::CustomTypeDescriptorSelector; #endif @@ -143,8 +140,6 @@ namespace gaclib_controls friend class ::vl_workflow_global::__vwsnc3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; friend class ::vl_workflow_global::__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; friend class ::vl_workflow_global::__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; - friend class ::vl_workflow_global::__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; - friend struct ::vl_workflow_global::__vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; friend struct ::vl_workflow_global::__vwsnf1_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; friend struct ::vl_workflow_global::__vwsnf2_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; friend struct ::vl_workflow_global::__vwsnf3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; @@ -158,8 +153,11 @@ namespace gaclib_controls friend struct ::vl::reflection::description::CustomTypeDescriptorSelector; #endif protected: - ::gaclib_controls::UnitTestSnapshotViewerWindow* self; ::vl::Ptr<::gaclib_controls::IUnitTestSnapshotViewerViewModel> ViewModel; + ::gaclib_controls::UnitTestSnapshotViewerWindow* self; + ::vl::presentation::controls::GuiBindableTreeView* treeViewFileNodes; + ::vl::presentation::controls::GuiBindableTextList* textListFrames; + ::vl::presentation::controls::GuiScrollContainer* scRendering; ::vl::presentation::compositions::GuiTableComposition* __vwsn_precompile_0; ::vl::presentation::compositions::GuiColumnSplitterComposition* __vwsn_precompile_1; ::vl::presentation::compositions::GuiColumnSplitterComposition* __vwsn_precompile_2; @@ -181,12 +179,6 @@ namespace gaclib_controls ::vl::presentation::controls::GuiTabPage* __vwsn_precompile_18; ::vl::presentation::controls::GuiMultilineTextBox* __vwsn_precompile_19; ::vl::presentation::compositions::GuiBoundsComposition* __vwsn_precompile_20; - ::vl::presentation::controls::GuiTabPage* __vwsn_precompile_21; - ::vl::presentation::controls::GuiMultilineTextBox* __vwsn_precompile_22; - ::vl::presentation::compositions::GuiBoundsComposition* __vwsn_precompile_23; - ::vl::presentation::controls::GuiBindableTreeView* treeViewFileNodes; - ::vl::presentation::controls::GuiBindableTextList* textListFrames; - ::vl::presentation::controls::GuiScrollContainer* scRendering; void __vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize(::gaclib_controls::UnitTestSnapshotViewerWindow* __vwsn_this_); public: UnitTestSnapshotViewerWindowConstructor(); @@ -200,8 +192,6 @@ namespace gaclib_controls friend class ::vl_workflow_global::__vwsnc3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; friend class ::vl_workflow_global::__vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; friend class ::vl_workflow_global::__vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; - friend class ::vl_workflow_global::__vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription; - friend struct ::vl_workflow_global::__vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; friend struct ::vl_workflow_global::__vwsnf1_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; friend struct ::vl_workflow_global::__vwsnf2_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; friend struct ::vl_workflow_global::__vwsnf3_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_; @@ -251,15 +241,6 @@ namespace vl_workflow_global Closures ***********************************************************************/ - struct __vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_ - { - ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnthis_0; - - __vwsnf10_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0); - - void operator()(const ::vl::reflection::description::Value& __vwsn_value_) const; - }; - struct __vwsnf1_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize_ { ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnthis_0; @@ -397,29 +378,11 @@ Closures class __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription : public ::vl::Object, public virtual ::vl::reflection::description::IValueSubscription { - public: - ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnthis_0; - - __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0); - - ::vl::presentation::controls::GuiBindableTextList* __vwsn_bind_cache_0 = nullptr; - ::vl::Ptr<::vl::reflection::description::IEventHandler> __vwsn_bind_handler_0_0; - bool __vwsn_bind_opened_ = false; - bool __vwsn_bind_closed_ = false; - void __vwsn_bind_activator_(); - void __vwsn_bind_callback_0_0(::vl::presentation::compositions::GuiGraphicsComposition* __vwsn_bind_callback_argument_0, ::vl::presentation::compositions::GuiEventArgs* __vwsn_bind_callback_argument_1); - bool Open() override; - bool Update() override; - bool Close() override; - }; - - class __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription : public ::vl::Object, public virtual ::vl::reflection::description::IValueSubscription - { public: ::gaclib_controls::UnitTestSnapshotViewerWindow* __vwsn_this_; ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnthis_0; - __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindow* __vwsnctor___vwsn_this_, ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0); + __vwsnc4_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindow* __vwsnctor___vwsn_this_, ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0); ::gaclib_controls::UnitTestSnapshotViewerWindow* __vwsn_bind_cache_0 = nullptr; ::vl::Ptr<::vl::reflection::description::IEventHandler> __vwsn_bind_handler_0_0; @@ -432,12 +395,12 @@ Closures bool Close() override; }; - class __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription : public ::vl::Object, public virtual ::vl::reflection::description::IValueSubscription + class __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription : public ::vl::Object, public virtual ::vl::reflection::description::IValueSubscription { public: ::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnthis_0; - __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0); + __vwsnc5_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerWindowConstructor___vwsn_gaclib_controls_UnitTestSnapshotViewerWindow_Initialize__vl_reflection_description_IValueSubscription(::gaclib_controls::UnitTestSnapshotViewerWindowConstructor* __vwsnctorthis_0); ::vl::presentation::controls::GuiApplication* __vwsn_bind_cache_0 = nullptr; ::vl::Ptr<::vl::reflection::description::IEventHandler> __vwsn_bind_handler_0_0; @@ -450,10 +413,10 @@ Closures bool Close() override; }; - class __vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings : public ::vl::Object, public virtual ::gaclib_controls::IUnitTestSnapshotViewerStringsStrings + class __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings : public ::vl::Object, public virtual ::gaclib_controls::IUnitTestSnapshotViewerStringsStrings { public: - __vwsnc7_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings(); + __vwsnc6_GuiUnitTestSnapshotViewer_gaclib_controls_UnitTestSnapshotViewerStrings___vwsn_ls_en_US_BuildStrings__gaclib_controls_IUnitTestSnapshotViewerStringsStrings(); ::vl::WString WindowTitle() override; }; @@ -495,8 +458,8 @@ https://github.com/vczh-libraries namespace vl::presentation::remoteprotocol { - struct RenderingFrame; - struct RenderingTrace; + struct UnitTest_RenderingFrame; + struct UnitTest_RenderingTrace; } namespace vl::presentation::unittest @@ -515,8 +478,8 @@ namespace vl::presentation::unittest Ptr GetRootNode() override; }; - extern const remoteprotocol::RenderingTrace& GetRenderingTrace(Ptr node); - extern const remoteprotocol::RenderingFrame& GetRenderingFrame(Ptr frame); + extern const remoteprotocol::UnitTest_RenderingTrace& GetRenderingTrace(Ptr node); + extern const remoteprotocol::UnitTest_RenderingFrame& GetRenderingFrame(Ptr frame); } /*********************************************************************** diff --git a/Import/GacUI.UnitTest.UIReflection.cpp b/Import/GacUI.UnitTest.UIReflection.cpp index 20c542be..013c5a85 100644 --- a/Import/GacUI.UnitTest.UIReflection.cpp +++ b/Import/GacUI.UnitTest.UIReflection.cpp @@ -64,11 +64,9 @@ namespace vl BEGIN_INTERFACE_MEMBER(::gaclib_controls::IUnitTestSnapshotFrame) CLASS_MEMBER_BASE(::vl::reflection::IDescriptable) - CLASS_MEMBER_METHOD(GetCommandsAsJsonText, NO_PARAMETER) CLASS_MEMBER_METHOD(GetDomAsJsonText, NO_PARAMETER) CLASS_MEMBER_METHOD(GetElementsAsJsonText, NO_PARAMETER) CLASS_MEMBER_METHOD(GetName, NO_PARAMETER) - CLASS_MEMBER_PROPERTY_READONLY(CommandsAsJsonText, GetCommandsAsJsonText) CLASS_MEMBER_PROPERTY_READONLY(DomAsJsonText, GetDomAsJsonText) CLASS_MEMBER_PROPERTY_READONLY(ElementsAsJsonText, GetElementsAsJsonText) CLASS_MEMBER_PROPERTY_READONLY(Name, GetName) @@ -133,9 +131,6 @@ namespace vl CLASS_MEMBER_FIELD(__vwsn_precompile_19) CLASS_MEMBER_FIELD(__vwsn_precompile_2) CLASS_MEMBER_FIELD(__vwsn_precompile_20) - CLASS_MEMBER_FIELD(__vwsn_precompile_21) - CLASS_MEMBER_FIELD(__vwsn_precompile_22) - CLASS_MEMBER_FIELD(__vwsn_precompile_23) CLASS_MEMBER_FIELD(__vwsn_precompile_3) CLASS_MEMBER_FIELD(__vwsn_precompile_4) CLASS_MEMBER_FIELD(__vwsn_precompile_5) diff --git a/Import/GacUI.UnitTest.UIReflection.h b/Import/GacUI.UnitTest.UIReflection.h index d5402d90..65d57e6a 100644 --- a/Import/GacUI.UnitTest.UIReflection.h +++ b/Import/GacUI.UnitTest.UIReflection.h @@ -86,10 +86,6 @@ namespace vl END_INTERFACE_PROXY(::gaclib_controls::IUnitTestSnapshotFileNode) BEGIN_INTERFACE_PROXY_NOPARENT_SHAREDPTR(::gaclib_controls::IUnitTestSnapshotFrame) - ::vl::WString GetCommandsAsJsonText() override - { - INVOKEGET_INTERFACE_PROXY_NOPARAMS(GetCommandsAsJsonText); - } ::vl::WString GetDomAsJsonText() override { INVOKEGET_INTERFACE_PROXY_NOPARAMS(GetDomAsJsonText); diff --git a/Import/GacUI.UnitTest.cpp b/Import/GacUI.UnitTest.cpp index 32d427d0..3d3fe2c9 100644 --- a/Import/GacUI.UnitTest.cpp +++ b/Import/GacUI.UnitTest.cpp @@ -146,9 +146,167 @@ File GacUIUnitTest_PrepareSnapshotFile(const WString& appName, const WString& ex #undef ERROR_MESSAGE_PREFIX } +void GacUIUnitTest_WriteSnapshotFileIfChanged(File& snapshotFile, const WString& textLog) +{ +#define ERROR_MESSAGE_PREFIX L"GacUIUnitTest_WriteSnapshotFileIfChanged(File&, const WString&)#" + bool skipWriting = false; + if (snapshotFile.Exists()) + { + auto previousLog = snapshotFile.ReadAllTextByBom(); + if (previousLog == textLog) + { + skipWriting = true; + } + } + if (!skipWriting) + { + bool succeeded = snapshotFile.WriteAllText(textLog, true, stream::BomEncoder::Utf8); + CHECK_ERROR(succeeded, ERROR_MESSAGE_PREFIX L"Failed to write the snapshot file."); + } +#undef ERROR_MESSAGE_PREFIX +} + +void GacUIUnitTest_LogUI(const WString& appName, UnitTestRemoteProtocol& unitTestProtocol) +{ +#define ERROR_MESSAGE_PREFIX L"GacUIUnitTest_LogUI(const WString&, UnitTestRemoteProtocol&)#" + File snapshotFile = GacUIUnitTest_PrepareSnapshotFile(appName, WString::Unmanaged(L".json")); + + JsonFormatting formatting; + formatting.spaceAfterColon = true; + formatting.spaceAfterComma = true; + formatting.crlf = true; + formatting.compact = true; + + auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(unitTestProtocol.GetLoggedTrace()); + auto textLog = JsonToString(jsonLog, formatting); + { + remoteprotocol::UnitTest_RenderingTrace deserialized; + remoteprotocol::ConvertJsonToCustomType(jsonLog, deserialized); + auto jsonLog2 = remoteprotocol::ConvertCustomTypeToJson(deserialized); + auto textLog2 = JsonToString(jsonLog2, formatting); + CHECK_ERROR(textLog == textLog2, ERROR_MESSAGE_PREFIX L"Serialization and deserialization doesn't match."); + } + + GacUIUnitTest_WriteSnapshotFileIfChanged(snapshotFile, textLog); +#undef ERROR_MESSAGE_PREFIX +} + +void GacUIUnitTest_LogCommands(const WString& appName, UnitTestRemoteProtocol& unitTestProtocol) +{ + File snapshotFile = GacUIUnitTest_PrepareSnapshotFile(appName, WString::Unmanaged(L"[commands].txt")); + + JsonFormatting formatting; + formatting.spaceAfterColon = true; + formatting.spaceAfterComma = true; + formatting.crlf = false; + formatting.compact = true; + + auto textLog = stream::GenerateToStream([&unitTestProtocol, &formatting](stream::TextWriter& writer) + { + auto&& loggedFrames = unitTestProtocol.GetLoggedFrames(); + for (auto loggedFrame : loggedFrames) + { + writer.WriteLine(L"========================================"); + writer.WriteLine(itow(loggedFrame->frameId)); + writer.WriteLine(L"========================================"); + for (auto&& commandLog : loggedFrame->renderingCommandsLog) + { + writer.WriteLine(commandLog); + } + }; + }); + + GacUIUnitTest_WriteSnapshotFileIfChanged(snapshotFile, textLog); +} + +void GacUIUnitTest_LogDiffs(const WString& appName, UnitTestRemoteProtocol& unitTestProtocol) +{ + File snapshotFile = GacUIUnitTest_PrepareSnapshotFile(appName, WString::Unmanaged(L"[diffs].txt")); + + JsonFormatting formatting; + formatting.spaceAfterColon = true; + formatting.spaceAfterComma = true; + formatting.crlf = false; + formatting.compact = true; + + auto textLog = stream::GenerateToStream([&unitTestProtocol, &formatting](stream::TextWriter& writer) + { + Ptr dom; + DomIndex domIndex; + auto&& loggedFrames = unitTestProtocol.GetLoggedFrames(); + for (auto loggedFrame : loggedFrames) + { + writer.WriteLine(L"========================================"); + writer.WriteLine(itow(loggedFrame->frameId)); + writer.WriteLine(L"========================================"); + + if (!dom) + { + dom = loggedFrame->renderingDom; + BuildDomIndex(dom, domIndex); + + List>> lines; + lines.Add({ 0,dom }); + for (vint i = 0; i < lines.Count(); i++) + { + for (vint j = 0; j < lines[i].key; j++) + { + writer.WriteString(L" "); + } + + auto line = lines[i].value; + writer.WriteString(itow(line->id)); + writer.WriteString(L": "); + + auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(line->content); + writer.WriteLine(JsonToString(jsonLog, formatting)); + + if (line->children) + { + for (auto child : *line->children.Obj()) + { + lines.Add({ lines[i].key + 1,child }); + } + } + } + } + else + { + DomIndex nextDomIndex; + BuildDomIndex(loggedFrame->renderingDom, nextDomIndex); + + Ptr> diffList; + if (loggedFrame->renderingDiffs) + { + diffList = loggedFrame->renderingDiffs.Value().diffsInOrder; + } + else + { + RenderingDom_DiffsInOrder diffs; + DiffDom(dom, domIndex, loggedFrame->renderingDom, nextDomIndex, diffs); + diffList = diffs.diffsInOrder; + } + + if (diffList) + { + for (auto&& diff : *diffList.Obj()) + { + auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(diff); + writer.WriteLine(JsonToString(jsonLog, formatting)); + } + } + + dom = loggedFrame->renderingDom; + domIndex = std::move(nextDomIndex); + } + }; + }); + + GacUIUnitTest_WriteSnapshotFileIfChanged(snapshotFile, textLog); +} + void GacUIUnitTest_Start(const WString& appName, Nullable config) { -#define ERROR_MESSAGE_PREFIX L"GacUIUnitTest_Start(const WString&, Nullable)#" UnitTestScreenConfig globalConfig; if (config) { @@ -159,50 +317,131 @@ void GacUIUnitTest_Start(const WString& appName, Nullable globalConfig.FastInitialize(1024, 768); } + // Renderer UnitTestRemoteProtocol unitTestProtocol(appName, globalConfig); - repeatfiltering::GuiRemoteProtocolFilterVerifier verifierProtocol(unitTestProtocol.GetProtocol()); + auto jsonParser = Ptr(new glr::json::Parser); + + // Data Processing in Renderer + channeling::GuiRemoteJsonChannelFromProtocol channelReceiver(unitTestProtocol.GetProtocol()); + channeling::GuiRemoteJsonChannelStringDeserializer channelJsonDeserializer(&channelReceiver, jsonParser); + channeling::GuiRemoteUtfStringChannelDeserializer channelUtf8Deserializer(&channelJsonDeserializer); + + // Boundary between Binaries + + // Data Processing in Core + channeling::GuiRemoteUtfStringChannelSerializer channelUtf8Serializer(&channelUtf8Deserializer); + channeling::GuiRemoteJsonChannelStringSerializer channelJsonSerializer(&channelUtf8Serializer, jsonParser); + + // Boundary between threads + + channeling::GuiRemoteProtocolFromJsonChannel channelSender(&channelJsonSerializer); + + // Core + repeatfiltering::GuiRemoteProtocolFilterVerifier verifierProtocol( + globalConfig.useChannel == UnitTestRemoteChannel::None + ? unitTestProtocol.GetProtocol() + : &channelSender + ); repeatfiltering::GuiRemoteProtocolFilter filteredProtocol(&verifierProtocol); + GuiRemoteProtocolDomDiffConverter diffConverterProtocol(&filteredProtocol); UnitTestContextImpl unitTestContext(&unitTestProtocol); guiMainUnitTestContext = &unitTestContext; - SetupRemoteNativeController(&filteredProtocol); + SetupRemoteNativeController( + globalConfig.useDomDiff + ? static_cast(&diffConverterProtocol) + : &filteredProtocol + ); GacUIUnitTest_SetGuiMainProxy({}); + GacUIUnitTest_LogUI(appName, unitTestProtocol); + if (!globalConfig.useDomDiff) { - File snapshotFile = GacUIUnitTest_PrepareSnapshotFile(appName, WString::Unmanaged(L".json")); - - JsonFormatting formatting; - formatting.spaceAfterColon = true; - formatting.spaceAfterComma = true; - formatting.crlf = true; - formatting.compact = true; - - auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(unitTestProtocol.GetLoggedTrace()); - auto textLog = JsonToString(jsonLog, formatting); - { - remoteprotocol::RenderingTrace deserialized; - remoteprotocol::ConvertJsonToCustomType(jsonLog, deserialized); - auto jsonLog2 = remoteprotocol::ConvertCustomTypeToJson(deserialized); - auto textLog2 = JsonToString(jsonLog2, formatting); - CHECK_ERROR(textLog == textLog2, ERROR_MESSAGE_PREFIX L"Serialization and deserialization doesn't match."); - } - - bool skipWriting = false; - if (snapshotFile.Exists()) - { - auto previousLog = snapshotFile.ReadAllTextByBom(); - if (previousLog == textLog) - { - skipWriting = true; - } - } - if (!skipWriting) - { - bool succeeded = snapshotFile.WriteAllText(textLog, true, stream::BomEncoder::Utf8); - CHECK_ERROR(succeeded, ERROR_MESSAGE_PREFIX L"Failed to write the snapshot file."); - } + GacUIUnitTest_LogCommands(appName, unitTestProtocol); } -#undef ERROR_MESSAGE_PREFIX + GacUIUnitTest_LogDiffs(appName, unitTestProtocol); +} + +template +void RunInNewThread(T&& threadProc) +{ + Thread::CreateAndStart([threadProc]() + { + try + { + threadProc(); + } + catch (const Exception& e) + { + (void)e; + throw; + } + catch (const Error& e) + { + (void)e; + throw; + } + }); +} + +void GacUIUnitTest_StartAsync(const WString& appName, Nullable config) +{ + TEST_ASSERT(config && config.Value().useChannel == UnitTestRemoteChannel::Async); + + // Renderer + UnitTestRemoteProtocol unitTestProtocol(appName, config.Value()); + auto jsonParser = Ptr(new glr::json::Parser); + + // Data Processing in Renderer + channeling::GuiRemoteJsonChannelFromProtocol channelReceiver(unitTestProtocol.GetProtocol()); + channeling::GuiRemoteJsonChannelStringDeserializer channelJsonDeserializer(&channelReceiver, jsonParser); + channeling::GuiRemoteUtfStringChannelDeserializer channelUtf8Deserializer(&channelJsonDeserializer); + + // Boundary between Binaries + + // Data Processing in Core + channeling::GuiRemoteUtfStringChannelSerializer channelUtf8Serializer(&channelUtf8Deserializer); + channeling::GuiRemoteJsonChannelStringSerializer channelJsonSerializer(&channelUtf8Serializer, jsonParser); + + // Boundary between threads + + channeling::GuiRemoteProtocolAsyncJsonChannelSerializer asyncChannelSender; + asyncChannelSender.Start( + &channelJsonSerializer, + [&unitTestProtocol, config](channeling::GuiRemoteProtocolAsyncJsonChannelSerializer* channel) + { + channeling::GuiRemoteProtocolFromJsonChannel channelSender(channel); + + // Core + repeatfiltering::GuiRemoteProtocolFilterVerifier verifierProtocol(&channelSender); + repeatfiltering::GuiRemoteProtocolFilter filteredProtocol(&verifierProtocol); + GuiRemoteProtocolDomDiffConverter diffConverterProtocol(&filteredProtocol); + + UnitTestContextImpl unitTestContext(&unitTestProtocol); + guiMainUnitTestContext = &unitTestContext; + SetupRemoteNativeController( + config.Value().useDomDiff + ? static_cast(&diffConverterProtocol) + : &filteredProtocol + ); + GacUIUnitTest_SetGuiMainProxy({}); + }, + []( + channeling::GuiRemoteProtocolAsyncJsonChannelSerializer::TChannelThreadProc channelThreadProc, + channeling::GuiRemoteProtocolAsyncJsonChannelSerializer::TUIThreadProc uiThreadProc + ) + { + RunInNewThread(channelThreadProc); + RunInNewThread(uiThreadProc); + }); + + asyncChannelSender.WaitForStopped(); + GacUIUnitTest_LogUI(appName, unitTestProtocol); + if (!config.Value().useDomDiff) + { + GacUIUnitTest_LogCommands(appName, unitTestProtocol); + } + GacUIUnitTest_LogDiffs(appName, unitTestProtocol); } void GacUIUnitTest_Start_WithResourceAsText(const WString& appName, Nullable config, const WString& resourceText) @@ -240,7 +479,15 @@ void GacUIUnitTest_Start_WithResourceAsText(const WString& appName, Nullable(const WindowStyleConfig&) const = default; }; + enum class UnitTestRemoteChannel + { + None, + Sync, + Async, + }; + struct UnitTestScreenConfig { using FontConfig = vl::presentation::remoteprotocol::FontConfig; @@ -56,6 +63,8 @@ namespace vl::presentation::unittest NativeMargin customFramePadding; FontConfig fontConfig; ScreenConfig screenConfig; + bool useDomDiff = false; + UnitTestRemoteChannel useChannel = UnitTestRemoteChannel::None; void FastInitialize(vint width, vint height, vint taskBarHeight = 0); }; @@ -594,11 +603,18 @@ namespace vl::presentation::unittest UnitTestRemoteProtocol ***********************************************************************/ - using ElementDescVariant = remoteprotocol::ElementDescVariant; - using UnitTestRenderingCommand = remoteprotocol::RenderingCommand; - using UnitTestRenderingCommandList = collections::List; - using UnitTestRenderingCommandListRef = Ptr; + using ElementDescVariant = remoteprotocol::UnitTest_ElementDescVariant; using UnitTestRenderingDom = remoteprotocol::RenderingDom; + + struct UnitTestLoggedFrame + { + vint frameId; + collections::List renderingCommandsLog; + Nullable renderingDiffs; + Ptr renderingDom; + }; + + using UnitTestLoggedFrameList = collections::List>; template class UnitTestRemoteProtocol_Rendering : public TProtocol @@ -607,20 +623,25 @@ UnitTestRemoteProtocol using Base64ToImageMetadataMap = collections::Dictionary; using ElementDescMap = collections::Dictionary; using ImageMetadataMap = collections::Dictionary; - using CommandList = UnitTestRenderingCommandList; - using CommandListRef = UnitTestRenderingCommandListRef; + protected: + remoteprotocol::RenderingDomBuilder renderingDomBuilder; + remoteprotocol::UnitTest_RenderingTrace loggedTrace; + UnitTestLoggedFrameList loggedFrames; + bool lastRenderingCommandsOpening = false; - remoteprotocol::RenderingTrace loggedTrace; - ElementDescMap lastElementDescs; - IdSet removedElementIds; - IdSet removedImageIds; - Ptr cachedImageMetadatas; + Ptr receivedDom; + remoteprotocol::DomIndex receivedDomIndex; + bool receivedDomDiffMessage = false; + bool receivedElementMessage = false; - remoteprotocol::ElementMeasurings measuringForNextRendering; - regex::Regex regexCrLf{ L"/n|/r(/n)?" }; - vint lastFrameId = 0; - CommandListRef lastRenderingCommands; + ElementDescMap lastElementDescs; + IdSet removedElementIds; + IdSet removedImageIds; + Ptr cachedImageMetadatas; + + remoteprotocol::ElementMeasurings measuringForNextRendering; + regex::Regex regexCrLf{ L"/n|/r(/n)?" }; void ResetCreatedObjects() { @@ -636,7 +657,7 @@ UnitTestRemoteProtocol : TProtocol(std::forward(args)...) { ResetCreatedObjects(); - loggedTrace.frames = Ptr(new collections::List); + loggedTrace.frames = Ptr(new collections::List); } protected: @@ -645,49 +666,112 @@ UnitTestRemoteProtocol IGuiRemoteProtocolMessages (Rendering) ***********************************************************************/ + Ptr GetLastRenderingFrame() + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::GetLastRenderingCommands()#" + CHECK_ERROR(lastRenderingCommandsOpening, ERROR_MESSAGE_PREFIX L"The latest frame of commands is not accepting new commands."); + return loggedFrames[loggedFrames.Count() - 1]; +#undef ERROR_MESSAGE_PREFIX + } + + Ptr TryGetLastRenderingFrameAndReset() + { + if (loggedFrames.Count() == 0) return nullptr; + if (!lastRenderingCommandsOpening) return nullptr; + lastRenderingCommandsOpening = false; + return loggedFrames[loggedFrames.Count() - 1]; + } + void RequestRendererBeginRendering(const remoteprotocol::ElementBeginRendering& arguments) override { - lastFrameId = arguments.frameId; - lastRenderingCommands = Ptr(new CommandList); + receivedDomDiffMessage = false; + receivedElementMessage = false; + lastRenderingCommandsOpening = true; + auto frame = Ptr(new UnitTestLoggedFrame); + frame->frameId = arguments.frameId; + loggedFrames.Add(frame); } void RequestRendererEndRendering(vint id) override { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::RequestRendererEndRendering(vint)#" + CHECK_ERROR(receivedElementMessage || receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"Either dom-diff or element message should be sent before this message."); + + auto lastFrame = GetLastRenderingFrame(); + if (receivedElementMessage) + { + lastFrame->renderingDom = renderingDomBuilder.RequestRendererEndRendering(); + } + if (receivedDomDiffMessage) + { + // receivedDom will be updated in RequestRendererRenderDomDiff + // store a copy to log + lastFrame->renderingDom = CopyDom(receivedDom); + } this->GetEvents()->RespondRendererEndRendering(id, measuringForNextRendering); measuringForNextRendering = {}; +#undef ERROR_MESSAGE_PREFIX } +/*********************************************************************** +IGuiRemoteProtocolMessages (Rendering - Element) +***********************************************************************/ + void RequestRendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments) override { - lastRenderingCommands->Add(remoteprotocol::RenderingCommand_BeginBoundary{ arguments }); +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::RequestRendererBeginBoundary(const ElementBoundary&)#" + CHECK_ERROR(!receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with dom-diff messages in the same rendering cycle."); + if (!receivedElementMessage) + { + renderingDomBuilder.RequestRendererBeginRendering(); + receivedElementMessage = true; + } + + renderingDomBuilder.RequestRendererBeginBoundary(arguments); + + glr::json::JsonFormatting formatting; + formatting.spaceAfterColon = true; + formatting.spaceAfterComma = true; + formatting.crlf = false; + formatting.compact = true; + + GetLastRenderingFrame()->renderingCommandsLog.Add(L"RequestRendererBeginBoundary: " + stream::GenerateToStream([&](stream::TextWriter& writer) + { + auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(arguments); + writer.WriteString(glr::json::JsonToString(jsonLog, formatting)); + })); +#undef ERROR_MESSAGE_PREFIX } void RequestRendererEndBoundary() override { - lastRenderingCommands->Add(remoteprotocol::RenderingCommand_EndBoundary{}); +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::RequestRendererEndBoundary()#" + CHECK_ERROR(!receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with dom-diff messages in the same rendering cycle."); + if (!receivedElementMessage) + { + renderingDomBuilder.RequestRendererBeginRendering(); + receivedElementMessage = true; + } + + renderingDomBuilder.RequestRendererEndBoundary(); + GetLastRenderingFrame()->renderingCommandsLog.Add(L"RequestRendererEndBoundary"); +#undef ERROR_MESSAGE_PREFIX } - template - void RequestRendererRenderElement(const remoteprotocol::ElementRendering& rendering, const T& element) + void EnsureRenderedElement(vint id, Rect bounds) { - lastRenderingCommands->Add(remoteprotocol::RenderingCommand_Element{ rendering,element.id }); - } +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::EnsureRenderedElement(id&)#" - void RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments) override - { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::RequestRendererRenderElement(const ElementRendering&)#" - vint index = loggedTrace.createdElements->Keys().IndexOf(arguments.id); + vint index = loggedTrace.createdElements->Keys().IndexOf(id); CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been created."); - auto rendererType = loggedTrace.createdElements->Values()[index]; if (rendererType == remoteprotocol::RendererType::FocusRectangle) { // FocusRectangle does not has a ElementDesc - lastRenderingCommands->Add(remoteprotocol::RenderingCommand_Element{ arguments,arguments.id }); return; } - index = lastElementDescs.Keys().IndexOf(arguments.id); + index = lastElementDescs.Keys().IndexOf(id); CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Renderer with the specified id has not been updated after created."); lastElementDescs.Values()[index].Apply(Overloading( [](remoteprotocol::RendererType) @@ -696,13 +780,90 @@ IGuiRemoteProtocolMessages (Rendering) }, [&](const remoteprotocol::ElementDesc_SolidLabel& solidLabel) { - CalculateSolidLabelSizeIfNecessary(arguments.bounds.Width(), arguments.bounds.Height(), solidLabel); - RequestRendererRenderElement(arguments, solidLabel); + CalculateSolidLabelSizeIfNecessary(bounds.Width(), bounds.Height(), solidLabel); }, [&](const auto& element) { - RequestRendererRenderElement(arguments, element); })); + +#undef ERROR_MESSAGE_PREFIX + } + + void RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments) override + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::RequestRendererRenderElement(const ElementRendering&)#" + CHECK_ERROR(!receivedDomDiffMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with dom-diff messages in the same rendering cycle."); + if (!receivedElementMessage) + { + renderingDomBuilder.RequestRendererBeginRendering(); + receivedElementMessage = true; + } + + { + renderingDomBuilder.RequestRendererRenderElement(arguments); + + glr::json::JsonFormatting formatting; + formatting.spaceAfterColon = true; + formatting.spaceAfterComma = true; + formatting.crlf = false; + formatting.compact = true; + GetLastRenderingFrame()->renderingCommandsLog.Add(L"RequestRendererRenderElement: " + stream::GenerateToStream([&](stream::TextWriter& writer) + { + auto jsonLog = remoteprotocol::ConvertCustomTypeToJson(arguments); + writer.WriteString(glr::json::JsonToString(jsonLog, formatting)); + })); + } + + EnsureRenderedElement(arguments.id, arguments.bounds); +#undef ERROR_MESSAGE_PREFIX + } + +/*********************************************************************** +IGuiRemoteProtocolMessages (Rendering - Dom) +***********************************************************************/ + + void CalculateSolidLabelSizesIfNecessary(Ptr dom) + { + if (dom->content.element) + { + EnsureRenderedElement(dom->content.element.Value(), dom->content.bounds); + } + if (dom->children) + { + for (auto child : *dom->children.Obj()) + { + CalculateSolidLabelSizesIfNecessary(child); + } + } + } + + void RequestRendererRenderDom(const Ptr& arguments) override + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::RequestRendererRenderElement(const RenderingDom&)#" + CHECK_ERROR(!receivedElementMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with element messages in the same rendering cycle."); + if (!receivedDomDiffMessage) + { + receivedDomDiffMessage = true; + } + + receivedDom = arguments; + remoteprotocol::BuildDomIndex(receivedDom, receivedDomIndex); + CalculateSolidLabelSizesIfNecessary(receivedDom); +#undef ERROR_MESSAGE_PREFIX + } + + void RequestRendererRenderDomDiff(const remoteprotocol::RenderingDom_DiffsInOrder& arguments) override + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Rendering::RequestRendererRenderElement(const RenderingDom_DiffsInOrder&)#" + CHECK_ERROR(!receivedElementMessage, ERROR_MESSAGE_PREFIX L"This message could not be used with element messages in the same rendering cycle."); + if (!receivedDomDiffMessage) + { + receivedDomDiffMessage = true; + } + + remoteprotocol::UpdateDomInplace(receivedDom, receivedDomIndex, arguments); + GetLastRenderingFrame()->renderingDiffs = arguments; + CalculateSolidLabelSizesIfNecessary(receivedDom); #undef ERROR_MESSAGE_PREFIX } @@ -1138,205 +1299,31 @@ UnitTestRemoteProtocol template class UnitTestRemoteProtocol_Logging : public TProtocol { - using CommandList = UnitTestRenderingCommandList; - using CommandListRef = UnitTestRenderingCommandListRef; - using RenderingResultRef = Ptr; - using RenderingResultRefList = collections::List; - using LoggedFrameList = collections::List; protected: - bool everRendered = false; - vint candidateFrameId = 0; - CommandListRef candidateRenderingResult; - - RenderingResultRef TransformLastRenderingResult(CommandListRef commandListRef) - { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::unittest::UnitTestRemoteProtocol_Logging::TransformLastRenderingResult(CommandListRef)#" - - RenderingResultRefList domStack; - collections::List domBoundaries; - - auto domRoot = Ptr(new UnitTestRenderingDom); - auto domCurrent = domRoot; - domStack.Add(domRoot); - - auto getCurrentBoundary = [&]() -> vint - { - if (domBoundaries.Count() > 0) - { - return domBoundaries[domBoundaries.Count() - 1]; - } - else - { - return 0; - } - }; - - auto push = [&](RenderingResultRef ref) - { - CHECK_ERROR(ref, ERROR_MESSAGE_PREFIX L"[push] Cannot push a null dom object."); - vint index = domStack.Add(ref); - if (!domCurrent->children) domCurrent->children = Ptr(new RenderingResultRefList); - domCurrent->children->Add(ref); - domCurrent = ref; - return index; - }; - - auto popTo = [&](vint index) - { - if (index == domStack.Count() - 1) return; - CHECK_ERROR(0 <= index && index < domStack.Count(), ERROR_MESSAGE_PREFIX L"[popTo] Cannot pop to an invalid position."); - CHECK_ERROR(index >= getCurrentBoundary(), ERROR_MESSAGE_PREFIX L"[popTo] Cannot pop across a boundary."); - while (domStack.Count() - 1 > index) - { - domStack.RemoveAt(domStack.Count() - 1); - } - domCurrent = domStack[index]; - }; - - auto pop = [&]() - { - popTo(domStack.Count() - 2); - }; - - auto popBoundary = [&]() - { - CHECK_ERROR(domBoundaries.Count() > 0, ERROR_MESSAGE_PREFIX L"[popBoundary] Cannot pop a boundary when none is in the stack."); - auto boundaryIndex = domBoundaries.Count() - 1; - auto boundary = domBoundaries[boundaryIndex]; - domBoundaries.RemoveAt(boundaryIndex); - popTo(boundary - 1); - }; - - auto prepareParentFromCommand = [&]( - Rect commandBounds, - Rect commandValidArea, - auto&& calculateValidAreaFromDom - ) - { - vint min = getCurrentBoundary(); - bool found = false; - if (commandValidArea.Contains(commandBounds)) - { - // if the command is not clipped - for (vint i = domStack.Count() - 1; i >= min; i--) - { - if (domStack[i]->validArea.Contains(commandBounds) || i == 0) - { - // find the deepest node that could contain the command - popTo(i); - found = true; - break; - } - } - } - else - { - // otherwise, a parent node causing such clipping should be found or created - for (vint i = domStack.Count() - 1; i >= min; i--) - { - auto domValidArea = calculateValidAreaFromDom(domStack[i]); - if (domValidArea == commandValidArea) - { - // if there is a node who clips command's bound to its valid area - // that is the parent node of the command - popTo(i); - found = true; - break; - } - else if (domValidArea.Contains(commandValidArea) || i == 0) - { - // otherwise find a deepest node who could visually contain the command - // create a virtual node to satisfy the clipper - popTo(i); - auto parent = Ptr(new UnitTestRenderingDom); - parent->bounds = commandValidArea; - parent->validArea = commandValidArea; - push(parent); - found = true; - break; - } - } - } - - // if the new boundary could not fit in the current boundary - // there must be something wrong - CHECK_ERROR(found, ERROR_MESSAGE_PREFIX L"Incorrect valid area of dom."); - }; - - for (auto&& command : *commandListRef.Obj()) - { - command.Apply(Overloading( - [&](const remoteprotocol::RenderingCommand_BeginBoundary& command) - { - // a new boundary should be a new node covering existing nodes - // the valid area of boundary is clipped by its bounds - // so the valid area to compare from its potential parent dom needs to clipped by its bounds - prepareParentFromCommand( - command.boundary.bounds, - command.boundary.areaClippedBySelf, - [&](auto&& dom) { return dom->validArea.Intersect(command.boundary.bounds); } - ); - - auto dom = Ptr(new UnitTestRenderingDom); - dom->hitTestResult = command.boundary.hitTestResult; - dom->cursor = command.boundary.cursor; - dom->bounds = command.boundary.bounds; - dom->validArea = command.boundary.areaClippedBySelf; - domBoundaries.Add(push(dom)); - }, - [&](const remoteprotocol::RenderingCommand_EndBoundary& command) - { - popBoundary(); - }, - [&](const remoteprotocol::RenderingCommand_Element& command) - { - // a new element should be a new node covering existing nodes - // the valid area of boundary is clipped by its parent - // so the valid area to compare from its potential parent dom is its valid area - prepareParentFromCommand( - command.rendering.bounds, - command.rendering.areaClippedByParent, - [&](auto&& dom) { return dom->validArea; } - ); - - auto dom = Ptr(new UnitTestRenderingDom); - dom->element = command.element; - dom->bounds = command.rendering.bounds; - dom->validArea = command.rendering.bounds.Intersect(command.rendering.areaClippedByParent); - push(dom); - })); - } - - return domRoot; -#undef ERROR_MESSAGE_PREFIX - } + Ptr candidateFrame; bool LogRenderingResult() { - if (this->lastRenderingCommands) + if (auto lastFrame = this->TryGetLastRenderingFrameAndReset()) { - candidateFrameId = this->lastFrameId; - candidateRenderingResult = this->lastRenderingCommands; - this->lastRenderingCommands = {}; + candidateFrame = lastFrame; everRendered = true; } else if (everRendered) { - if (candidateRenderingResult) + if (candidateFrame) { - auto descs = Ptr(new collections::Dictionary); + auto descs = Ptr(new collections::Dictionary); CopyFrom(*descs.Obj(), this->lastElementDescs); - auto transformed = TransformLastRenderingResult(candidateRenderingResult); this->loggedTrace.frames->Add({ - candidateFrameId, + candidateFrame->frameId, {}, this->sizingConfig, descs, - candidateRenderingResult, - transformed + candidateFrame->renderingDom }); - candidateRenderingResult = {}; + candidateFrame = {}; return true; } } @@ -1355,6 +1342,11 @@ UnitTestRemoteProtocol { return this->loggedTrace; } + + const auto& GetLoggedFrames() + { + return this->loggedFrames; + } }; } @@ -1468,8 +1460,9 @@ IGuiRemoteProtocolMessages (Initialization) IGuiRemoteProtocol ***********************************************************************/ - void Submit() override + void Submit(bool& disconnected) override { + // TODO: Failure injection to disconnected } void ProcessRemoteEvents() override @@ -1529,6 +1522,7 @@ extern void GacUIUnitTest_Finalize(); extern void GacUIUnitTest_SetGuiMainProxy(const vl::presentation::unittest::UnitTestMainFunc& proxy); extern void GacUIUnitTest_LinkGuiMainProxy(const vl::presentation::unittest::UnitTestLinkFunc& proxy); extern void GacUIUnitTest_Start(const vl::WString& appName, vl::Nullable config = {}); +extern void GacUIUnitTest_StartAsync(const vl::WString& appName, vl::Nullable config = {}); extern void GacUIUnitTest_Start_WithResourceAsText(const vl::WString& appName, vl::Nullable config, const vl::WString& resourceText); extern vl::Ptr GacUIUnitTest_CompileAndLoad(const vl::WString& xmlResource); diff --git a/Import/GacUI.Windows.cpp b/Import/GacUI.Windows.cpp index e4312949..5cb8fe5f 100644 --- a/Import/GacUI.Windows.cpp +++ b/Import/GacUI.Windows.cpp @@ -999,13 +999,14 @@ WindowsForm return true; } break; + case WM_NCLBUTTONUP: case WM_LBUTTONUP: { - POINTS location = MAKEPOINTS(lParam); + NativeWindowMouseInfo info = ConvertMouse(wParam, lParam, false, nonClient); // TODO: (enumerable) this for-loop needs to be removed, because it is not looping, just leave the body for (vint i = 0; i < listeners.Count(); i++) { - switch (PerformHitTest(From(listeners), { location.x,location.y })) + switch (PerformHitTest(From(listeners), { info.x,info.y })) { case INativeWindowListener::ButtonMinimum: ShowMinimized(); @@ -2867,7 +2868,7 @@ using namespace vl::presentation; using namespace vl::presentation::windows; using namespace vl::presentation::elements_windows_d2d; -int SetupWindowsDirect2DRendererInternal(bool hosted) +int SetupWindowsDirect2DRendererInternal(bool hosted, bool raw) { InitDpiAwareness(true); CoInitializeEx(NULL, COINIT_MULTITHREADED); @@ -2897,7 +2898,7 @@ int SetupWindowsDirect2DRendererInternal(bool hosted) nativeController->CallbackService()->InstallListener(&listener); direct2DListener = &listener; // main - RendererMainDirect2D(hostedController); + RendererMainDirect2D(hostedController, raw); // uninstall listener direct2DListener = nullptr; nativeController->CallbackService()->UninstallListener(&listener); @@ -2916,12 +2917,17 @@ int SetupWindowsDirect2DRendererInternal(bool hosted) int SetupWindowsDirect2DRenderer() { - return SetupWindowsDirect2DRendererInternal(false); + return SetupWindowsDirect2DRendererInternal(false, false); } int SetupHostedWindowsDirect2DRenderer() { - return SetupWindowsDirect2DRendererInternal(true); + return SetupWindowsDirect2DRendererInternal(true, false); +} + +int SetupRawWindowsDirect2DRenderer() +{ + return SetupWindowsDirect2DRendererInternal(false, true); } /*********************************************************************** @@ -5127,24 +5133,27 @@ GuiImageFrameElementRenderer void GuiImageFrameElementRenderer::UpdateBitmap(IWindowsDirect2DRenderTarget* renderTarget) { - if(renderTarget && element->GetImage()) + if (element->GetImage() && 0 <= element->GetFrameIndex() && element->GetFrameIndex() < element->GetImage()->GetFrameCount()) { - INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex()); - bitmap=renderTarget->GetBitmap(frame, element->GetEnabled()); + INativeImageFrame* frame = element->GetImage()->GetFrame(element->GetFrameIndex()); + if (renderTarget) + { + bitmap = renderTarget->GetBitmap(frame, element->GetEnabled()); + } if (element->GetStretch()) { - minSize=Size(0,0); + minSize = Size(0, 0); } else { - minSize=frame->GetSize(); + minSize = frame->GetSize(); } } else { - bitmap=nullptr; - minSize=Size(0, 0); + bitmap = nullptr; + minSize = Size(0, 0); } } @@ -6439,9 +6448,11 @@ NativeMain using namespace vl::presentation; using namespace vl::presentation::elements; + +extern void GuiRawMain(); extern void GuiApplicationMain(); -void RendererMainDirect2D(GuiHostedController* hostedController) +void RendererMainDirect2D(GuiHostedController* hostedController, bool raw) { elements_windows_d2d::WindowsDirect2DResourceManager resourceManager; elements_windows_d2d::SetWindowsDirect2DResourceManager(&resourceManager); @@ -6470,7 +6481,14 @@ void RendererMainDirect2D(GuiHostedController* hostedController) elements::GuiDocumentElement::GuiDocumentElementRenderer::Register(); if (hostedController) hostedController->Initialize(); - GuiApplicationMain(); + if (raw) + { + GuiRawMain(); + } + else + { + GuiApplicationMain(); + } if (hostedController) hostedController->Finalize(); } @@ -8627,7 +8645,7 @@ using namespace vl::presentation; using namespace vl::presentation::windows; using namespace vl::presentation::elements_windows_gdi; -int SetupWindowsGDIRendererInternal(bool hosted) +int SetupWindowsGDIRendererInternal(bool hosted, bool raw) { InitDpiAwareness(false); CoInitializeEx(NULL, COINIT_MULTITHREADED); @@ -8657,7 +8675,7 @@ int SetupWindowsGDIRendererInternal(bool hosted) nativeController->CallbackService()->InstallListener(&listener); gdiListener = &listener; // main - RendererMainGDI(hostedController); + RendererMainGDI(hostedController, raw); // uninstall listener gdiListener = nullptr; nativeController->CallbackService()->UninstallListener(&listener); @@ -8676,12 +8694,17 @@ int SetupWindowsGDIRendererInternal(bool hosted) int SetupWindowsGDIRenderer() { - return SetupWindowsGDIRendererInternal(false); + return SetupWindowsGDIRendererInternal(false, false); } int SetupHostedWindowsGDIRenderer() { - return SetupWindowsGDIRendererInternal(true); + return SetupWindowsGDIRendererInternal(true, false); +} + +int SetupRawWindowsGDIRenderer() +{ + return SetupWindowsGDIRendererInternal(false, true); } /*********************************************************************** @@ -9658,25 +9681,25 @@ GuiImageFrameElementRenderer void GuiImageFrameElementRenderer::UpdateBitmap() { - if(element->GetImage()) + if (element->GetImage() && 0 <= element->GetFrameIndex() && element->GetFrameIndex() < element->GetImage()->GetFrameCount()) { - auto resourceManager=GetWindowsGDIResourceManager(); - INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex()); - bitmap=resourceManager->GetBitmap(frame, element->GetEnabled()); + auto resourceManager = GetWindowsGDIResourceManager(); + INativeImageFrame* frame = element->GetImage()->GetFrame(element->GetFrameIndex()); + bitmap = resourceManager->GetBitmap(frame, element->GetEnabled()); if (element->GetStretch()) { - minSize=Size(0,0); + minSize = Size(0, 0); } else { - minSize=frame->GetSize(); + minSize = frame->GetSize(); } } else { - bitmap=0; - minSize=Size(0, 0); + bitmap = nullptr; + minSize = Size(0, 0); } } @@ -9740,7 +9763,7 @@ GuiImageFrameElementRenderer } destination=Rect(x, y, x+minSize.x, y+minSize.y); } - if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0) + if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0) { auto resourceManager=GetWindowsGDIResourceManager(); vint max=element->GetFrameIndex(); @@ -13041,9 +13064,10 @@ NativeMain using namespace vl::presentation; using namespace vl::presentation::elements; +extern void GuiRawMain(); extern void GuiApplicationMain(); -void RendererMainGDI(GuiHostedController* hostedController) +void RendererMainGDI(GuiHostedController* hostedController, bool raw) { elements_windows_gdi::WindowsGDIResourceManager resourceManager; elements_windows_gdi::SetWindowsGDIResourceManager(&resourceManager); @@ -13072,7 +13096,14 @@ void RendererMainGDI(GuiHostedController* hostedController) elements::GuiDocumentElement::GuiDocumentElementRenderer::Register(); if (hostedController) hostedController->Initialize(); - GuiApplicationMain(); + if (raw) + { + GuiRawMain(); + } + else + { + GuiApplicationMain(); + } if (hostedController) hostedController->Finalize(); } diff --git a/Import/GacUI.Windows.h b/Import/GacUI.Windows.h index f96ee995..74b7706e 100644 --- a/Import/GacUI.Windows.h +++ b/Import/GacUI.Windows.h @@ -229,7 +229,7 @@ OS Supporting } } -extern void RendererMainDirect2D(vl::presentation::GuiHostedController* hostedController); +extern void RendererMainDirect2D(vl::presentation::GuiHostedController* hostedController, bool raw); #endif @@ -1512,7 +1512,7 @@ OS Supporting } } -extern void RendererMainGDI(vl::presentation::GuiHostedController* hostedController); +extern void RendererMainGDI(vl::presentation::GuiHostedController* hostedController, bool raw); #endif diff --git a/Import/GacUI.cpp b/Import/GacUI.cpp index e97aa443..0ebda376 100644 --- a/Import/GacUI.cpp +++ b/Import/GacUI.cpp @@ -657,10 +657,59 @@ GuiApplicationMain ThreadLocalStorage::DisposeStorages(); } } + + void GuiRawInitialize() + { + if (!GACUI_UNITTEST_ONLY_SKIP_TYPE_AND_PLUGIN_LOAD_UNLOAD) + { +#ifndef VCZH_DEBUG_NO_REFLECTION + GetGlobalTypeManager()->Load(); +#endif + GetPluginManager()->Load(true, true); + } + else + { + GetPluginManager()->Load(false, true); + } + + GetCurrentController()->InputService()->StartTimer(); + { + IAsyncScheduler::RegisterSchedulerForCurrentThread(Ptr(new UIThreadAsyncScheduler)); + IAsyncScheduler::RegisterDefaultScheduler(Ptr(new OtherThreadAsyncScheduler)); + GuiMain(); + IAsyncScheduler::UnregisterDefaultScheduler(); + IAsyncScheduler::UnregisterSchedulerForCurrentThread(); + } + GetCurrentController()->InputService()->StopTimer(); + FinalizeGlobalStorage(); + + if (!GACUI_UNITTEST_ONLY_SKIP_TYPE_AND_PLUGIN_LOAD_UNLOAD) + { + GetPluginManager()->Unload(true, true); + DestroyPluginManager(); +#ifndef VCZH_DEBUG_NO_REFLECTION + ResetGlobalTypeManager(); +#endif + } + else + { + GetPluginManager()->Unload(false, true); + } + + if (!GACUI_UNITTEST_ONLY_SKIP_THREAD_LOCAL_STORAGE_DISPOSE_STORAGES) + { + ThreadLocalStorage::DisposeStorages(); + } + } } } } +void GuiRawMain() +{ + vl::presentation::controls::GuiRawInitialize(); +} + void GuiApplicationMain() { vl::presentation::controls::GuiApplicationInitialize(); @@ -2533,9 +2582,9 @@ GuiControlHost void GuiControlHost::Hide() { - if(host->GetNativeWindow()) + if (auto window = host->GetNativeWindow()) { - host->GetNativeWindow()->Hide(false); + window->Hide(false); } } @@ -2543,16 +2592,17 @@ GuiControlHost { if (auto window = host->GetNativeWindow()) { - auto mainWindow = GetCurrentController()->WindowService()->GetMainWindow(); - if (mainWindow == window) - { - SetNativeWindow(nullptr); - GetCurrentController()->WindowService()->DestroyNativeWindow(window); - } - else - { - window->Hide(true); - } + window->Hide(true); + // auto mainWindow = GetCurrentController()->WindowService()->GetMainWindow(); + // if (mainWindow == window) + // { + // SetNativeWindow(nullptr); + // GetCurrentController()->WindowService()->DestroyNativeWindow(window); + // } + // else + // { + // window->Hide(true); + // } } } @@ -33845,11 +33895,11 @@ GuiImageFrameElement ***********************************************************************/ GuiImageFrameElement::GuiImageFrameElement() - :frameIndex(0) - ,hAlignment(Alignment::Left) - ,vAlignment(Alignment::Top) - ,stretch(false) - ,enabled(true) + : frameIndex(0) + , hAlignment(Alignment::Left) + , vAlignment(Alignment::Top) + , stretch(false) + , enabled(true) { } @@ -33875,17 +33925,19 @@ GuiImageFrameElement void GuiImageFrameElement::SetImage(Ptr _image, vint _frameIndex) { - if(image!=_image || frameIndex!=_frameIndex) + if (image != _image || frameIndex != _frameIndex) { - if(!_image) + if (!_image) { - image=0; - frameIndex=0; + image = nullptr; + frameIndex = 0; } - else if(0<=_frameIndex && _frameIndex<_image->GetFrameCount()) + else if (0 <= _frameIndex) { - image=_image; - frameIndex=_frameIndex; + // do not check frame count because + // on remote protocol metadata could have not been loaded yet + image = _image; + frameIndex = _frameIndex; } InvokeOnElementStateChanged(); } @@ -33913,10 +33965,10 @@ GuiImageFrameElement void GuiImageFrameElement::SetAlignments(Alignment horizontal, Alignment vertical) { - if(hAlignment!=horizontal || vAlignment!=vertical) + if (hAlignment != horizontal || vAlignment != vertical) { - hAlignment=horizontal; - vAlignment=vertical; + hAlignment = horizontal; + vAlignment = vertical; InvokeOnElementStateChanged(); } } @@ -33928,9 +33980,9 @@ GuiImageFrameElement void GuiImageFrameElement::SetStretch(bool value) { - if(stretch!=value) + if (stretch != value) { - stretch=value; + stretch = value; InvokeOnElementStateChanged(); } } @@ -33942,9 +33994,9 @@ GuiImageFrameElement void GuiImageFrameElement::SetEnabled(bool value) { - if(enabled!=value) + if (enabled != value) { - enabled=value; + enabled = value; InvokeOnElementStateChanged(); } } @@ -36816,7 +36868,33 @@ GuiHostedController::INativeWindowService SettingHostedWindowsBeforeRunning(); wmManager->needRefresh = true; - nativeController->WindowService()->Run(nativeWindow); + try + { + nativeController->WindowService()->Run(nativeWindow); + } + catch (const Exception& e) + { + (void)e; + DestroyHostedWindowsAfterRunning(); + throw; + } + catch (const Error& e) + { + (void)e; + DestroyHostedWindowsAfterRunning(); + throw; + } + catch (const unittest::UnitTestAssertError& e) + { + (void)e; + DestroyHostedWindowsAfterRunning(); + throw; + } + catch (...) + { + DestroyHostedWindowsAfterRunning(); + throw; + } CHECK_ERROR((nativeWindow == nullptr) == (mainWindow == nullptr), ERROR_MESSAGE_PREFIX L"Hosted windows should have been destroyed if the native windows is destroyed."); DestroyHostedWindowsAfterRunning(); #undef ERROR_MESSAGE_PREFIX @@ -37270,7 +37348,9 @@ GuiHostedWindow if (this != controller->mainWindow) { - // for main window, the underlying INativeWindow will run the process + // when the main window is being closed + // the underlying INativeWindow will run the process + // so we don't need to worry about it here bool cancel = false; for (auto listener : listeners) { @@ -38199,7 +38279,9 @@ GuiRemoteController::INativeInputService bool GuiRemoteController::IsKeyPressing(VKEY code) { vint idIsKeyPressing = remoteMessages.RequestIOIsKeyPressing(code); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + if (disconnected) return false; bool result = remoteMessages.RetrieveIOIsKeyPressing(idIsKeyPressing); return result; } @@ -38207,7 +38289,9 @@ GuiRemoteController::INativeInputService bool GuiRemoteController::IsKeyToggled(VKEY code) { vint idIsKeyToggled = remoteMessages.RequestIOIsKeyToggled(code); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + if (disconnected) return false; bool result = remoteMessages.RetrieveIOIsKeyToggled(idIsKeyToggled); return result; } @@ -38265,7 +38349,9 @@ GuiRemoteController::INativeInputService hotKeyIds.Add(id, entry); UpdateGlobalShortcutKey(); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded return id; } @@ -38280,7 +38366,9 @@ GuiRemoteController::INativeInputService hotKeySet.Remove(entry); UpdateGlobalShortcutKey(); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded return true; } @@ -38390,6 +38478,7 @@ GuiRemoteController::INativeWindowService applicationRunning = true; window->Show(); while (RunOneCycle()); + asyncService.ExecuteAsyncTasks(); applicationRunning = false; } @@ -38398,8 +38487,9 @@ GuiRemoteController::INativeWindowService if (!connectionStopped) { remoteProtocol->ProcessRemoteEvents(); - remoteMessages.Submit(); - if (timerEnabled) + bool disconnected = false; + remoteMessages.Submit(disconnected); + if (timerEnabled && !disconnected) { callbackService.InvokeGlobalTimer(); } @@ -38417,7 +38507,9 @@ GuiRemoteController (events) UpdateGlobalShortcutKey(); vint idGetFontConfig = remoteMessages.RequestControllerGetFontConfig(); vint idGetScreenConfig = remoteMessages.RequestControllerGetScreenConfig(); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + if (disconnected) return; remoteFontConfig = remoteMessages.RetrieveControllerGetFontConfig(idGetFontConfig); remoteScreenConfig = remoteMessages.RetrieveControllerGetScreenConfig(idGetScreenConfig); remoteWindow.OnControllerConnect(); @@ -38446,6 +38538,7 @@ GuiRemoteController (events) void GuiRemoteController::OnControllerScreenUpdated(const remoteprotocol::ScreenConfig& arguments) { remoteScreenConfig = arguments; + remoteWindow.OnControllerScreenUpdated(arguments); } /*********************************************************************** @@ -38475,7 +38568,9 @@ GuiRemoteController { imageService.Finalize(); remoteMessages.RequestControllerConnectionStopped(); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded } /*********************************************************************** @@ -38594,9 +38689,9 @@ GuiRemoteMessages { } - void GuiRemoteMessages::Submit() + void GuiRemoteMessages::Submit(bool& disconnected) { - remote->remoteProtocol->Submit(); + remote->remoteProtocol->Submit(disconnected); } /*********************************************************************** @@ -38696,7 +38791,10 @@ GuiRemoteEvents (events) { remote->remoteMessages.RequestControllerConnectionEstablished(); remote->OnControllerConnect(); - remote->remoteMessages.Submit(); + bool disconnected = false; + remote->remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded + // if disconnected, OnControllerDisconnect will be called } void GuiRemoteEvents::OnControllerDisconnect() @@ -38938,7 +39036,9 @@ GuiRemoteGraphicsRenderTarget { CHECK_ERROR(hitTestResults.Count() == 0, L"vl::presentation::elements::GuiRemoteGraphicsRenderTarget::StartRenderingOnNativeWindow()#Internal error: hit test result stack is not cleared."); vint idRendering = remote->remoteMessages.RequestRendererEndRendering(); - remote->remoteMessages.Submit(); + bool disconnected = false; + remote->remoteMessages.Submit(disconnected); + if (disconnected) return RenderTargetFailure::None; auto measuring = remote->remoteMessages.RetrieveRendererEndRendering(idRendering); bool minSizeChanged = false; @@ -39044,6 +39144,13 @@ GuiRemoteGraphicsRenderTarget if (arguments.hitTestResult || arguments.cursor) { + // GetHitTestResultFromGenerator or GetCursorFromGenerator ensures generator must be a composition + auto composition = dynamic_cast(generator); + if (composition->remoteId == -1) + { + composition->remoteId = ++usedCompositionIds; + } + arguments.id = composition->remoteId; arguments.bounds = clipper; arguments.areaClippedBySelf = validArea; remote->remoteMessages.RequestRendererBeginBoundary(arguments); @@ -39291,6 +39398,8 @@ GuiSolidBorderElementRenderer { id = newRenderTarget->AllocateNewElementId(); newRenderTarget->RegisterRenderer(this); + updated = true; + renderTargetChanged = true; } } @@ -39322,6 +39431,7 @@ GuiSolidBorderElementRenderer void RENDERER_CLASS_TYPE::ResetUpdated() { updated = false; + renderTargetChanged = false; } RENDERER_TEMPLATE_HEADER @@ -39612,12 +39722,12 @@ GuiSolidLabelElementRenderer elementFont = GetCurrentController()->ResourceService()->GetDefaultFont(); } - if (fullContent || lastFont != elementFont) + if (renderTargetChanged || fullContent || lastFont != elementFont) { arguments.font = elementFont; } - if (fullContent || lastText != elementText) + if (renderTargetChanged || fullContent || lastText != elementText) { arguments.text = elementText; } @@ -39630,7 +39740,7 @@ GuiSolidLabelElementRenderer TryFetchMinSizeFromCache(); if (!needFontHeight) { - arguments.measuringRequest = {}; + arguments.measuringRequest.Reset(); } } @@ -39689,9 +39799,9 @@ GuiImageFrameElementRenderer { #define ERROR_MESSAGE_PREFIX L"vl::presentation::elements_remoteprotocol::GuiImageFrameElementRenderer::TryFetchMinSizeFromCache()#" auto image = GetRemoteImage(); - if (image) + if (!image || image->status != GuiRemoteGraphicsImage::MetadataStatus::Retrived) { - CHECK_ERROR(image->status == GuiRemoteGraphicsImage::MetadataStatus::Retrived, ERROR_MESSAGE_PREFIX L"The expected metadata of an image does not exist."); + return; } UpdateMinSizeFromImage(image); needUpdateSize = false; @@ -39704,13 +39814,24 @@ GuiImageFrameElementRenderer if (image) { needUpdateSize = true; - if (fullContent && image->status == GuiRemoteGraphicsImage::MetadataStatus::Retrived) + if (fullContent) { - image->status = GuiRemoteGraphicsImage::MetadataStatus::Uninitialized; + if (fullContent && image->status == GuiRemoteGraphicsImage::MetadataStatus::Retrived) + { + image->status = GuiRemoteGraphicsImage::MetadataStatus::Uninitialized; + } + } + else + { + if (image->status == GuiRemoteGraphicsImage::MetadataStatus::Uninitialized) + { + image->EnsureMetadata(); + } } if (image->status == GuiRemoteGraphicsImage::MetadataStatus::Retrived) { UpdateMinSizeFromImage(image); + renderTargetChanged = false; } } @@ -39745,7 +39866,7 @@ GuiImageFrameElementRenderer arguments.verticalAlignment = ElementVerticalAlignment::Center; } - if (needUpdateSize && image) + if ((renderTargetChanged || needUpdateSize) && image) { arguments.imageCreation = image->GenerateImageCreation(); } @@ -39864,7 +39985,9 @@ GuiRemoteGraphicsImage auto arguments = GenerateImageCreation(); vint idImageCreated = remote->remoteMessages.RequestImageCreated(arguments); - remote->remoteMessages.Submit(); + bool disconnected = false; + remote->remoteMessages.Submit(disconnected); + if (disconnected) return; auto imageMetadata = remote->remoteMessages.RetrieveImageCreated(idImageCreated); UpdateFromImageMetadata(imageMetadata); } @@ -40000,7 +40123,7 @@ GuiRemoteGraphicsImageService { } - void GuiRemoteGraphicsImageService::OnControllerConnect() + void GuiRemoteGraphicsImageService::ResetImageMetadata() { for (auto image : images.Values()) { @@ -40008,6 +40131,11 @@ GuiRemoteGraphicsImageService } } + void GuiRemoteGraphicsImageService::OnControllerConnect() + { + ResetImageMetadata(); + } + void GuiRemoteGraphicsImageService::OnControllerDisconnect() { } @@ -40250,6 +40378,1152 @@ namespace vl::presentation::remoteprotocol } +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_CHANNEL_ASYNC.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol::channeling +{ + using namespace vl::collections; + +/*********************************************************************** +GuiRemoteProtocolAsyncChannelSerializerBase +***********************************************************************/ + + void GuiRemoteProtocolAsyncChannelSerializerBase::QueueTask(SpinLock& lock, collections::List& tasks, TTaskProc task, EventObject* signalAfterQueue) + { + SPIN_LOCK(lock) + { + tasks.Add(task); + } + + if (signalAfterQueue) + { + signalAfterQueue->Signal(); + } + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::QueueTaskAndWait(SpinLock& lock, collections::List& tasks, TTaskProc task, EventObject* signalAfterQueue) + { + auto taskEvent = Ptr(new vl::EventObject); + taskEvent->CreateAutoUnsignal(false); + auto taskWithEvent = [=]() + { + task(); + taskEvent->Signal(); + }; + + QueueTask(lock, tasks, taskWithEvent, signalAfterQueue); + taskEvent->Wait(); + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::FetchTasks(SpinLock& lock, collections::List& tasks, collections::List& results) + { + SPIN_LOCK(lock) + { + results = std::move(tasks); + } + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::FetchAndExecuteTasks(SpinLock& lock, collections::List& tasks) + { + List results; + FetchTasks(lock, tasks, results); + for (auto&& task : results) + { + task(); + } + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::FetchAndExecuteChannelTasks() + { + FetchAndExecuteTasks(channelThreadLock, channelThreadTasks); + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::FetchAndExecuteUITasks() + { + FetchAndExecuteTasks(uiThreadLock, uiThreadTasks); + } + + GuiRemoteProtocolAsyncChannelSerializerBase::GuiRemoteProtocolAsyncChannelSerializerBase() + { + } + + GuiRemoteProtocolAsyncChannelSerializerBase::~GuiRemoteProtocolAsyncChannelSerializerBase() + { + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::QueueToChannelThread(TTaskProc task, EventObject* signalAfterQueue) + { + QueueTask(channelThreadLock, channelThreadTasks, task, signalAfterQueue); + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::QueueToChannelThreadAndWait(TTaskProc task, EventObject* signalAfterQueue) + { + QueueTaskAndWait(channelThreadLock, channelThreadTasks, task, signalAfterQueue); + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::QueueToUIThread(TTaskProc task, EventObject* signalAfterQueue) + { + QueueTask(uiThreadLock, uiThreadTasks, task, signalAfterQueue); + } + + void GuiRemoteProtocolAsyncChannelSerializerBase::QueueToUIThreadAndWait(TTaskProc task, EventObject* signalAfterQueue) + { + QueueTaskAndWait(uiThreadLock, uiThreadTasks, task, signalAfterQueue); + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_CHANNEL_JSON.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol::channeling +{ +/*********************************************************************** +ChannelPackageSemantic +***********************************************************************/ + + void JsonChannelPack(ChannelPackageSemantic semantic, vint id, const WString& name, Ptr arguments, Ptr& package) + { + package = Ptr(new glr::json::JsonObject); + + { + auto value = Ptr(new glr::json::JsonString); + switch (semantic) + { + case ChannelPackageSemantic::Message: + value->content.value = WString::Unmanaged(L"Message"); + break; + case ChannelPackageSemantic::Request: + value->content.value = WString::Unmanaged(L"Request"); + break; + case ChannelPackageSemantic::Response: + value->content.value = WString::Unmanaged(L"Response"); + break; + case ChannelPackageSemantic::Event: + value->content.value = WString::Unmanaged(L"Event"); + break; + default: + value->content.value = WString::Unmanaged(L"Unknown"); + } + + auto field = Ptr(new glr::json::JsonObjectField); + field->name.value = WString::Unmanaged(L"semantic"); + field->value = value; + package->fields.Add(field); + } + + if (id != -1) + { + auto value = Ptr(new glr::json::JsonNumber); + value->content.value = itow(id); + + auto field = Ptr(new glr::json::JsonObjectField); + field->name.value = WString::Unmanaged(L"id"); + field->value = value; + package->fields.Add(field); + } + + { + auto value = Ptr(new glr::json::JsonString); + value->content.value = name; + + auto field = Ptr(new glr::json::JsonObjectField); + field->name.value = WString::Unmanaged(L"name"); + field->value = value; + package->fields.Add(field); + } + + if (arguments) + { + auto field = Ptr(new glr::json::JsonObjectField); + field->name.value = WString::Unmanaged(L"arguments"); + field->value = arguments; + package->fields.Add(field); + } + } + + void JsonChannelUnpack(Ptr package, ChannelPackageSemantic& semantic, vint& id, WString& name, Ptr& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::channeling::JsonChannelPack(Ptr, ProtocolSemantic&, vint&, WString&, Ptr&)#" + + for (auto&& field : package->fields) + { + if (field->name.value == L"semantic") + { + auto value = field->value.Cast(); + CHECK_ERROR(value, ERROR_MESSAGE_PREFIX L"The semantic field should be a string."); + + if (value->content.value == L"Message") + { + semantic = ChannelPackageSemantic::Message; + } + else if (value->content.value == L"Request") + { + semantic = ChannelPackageSemantic::Request; + } + else if (value->content.value == L"Response") + { + semantic = ChannelPackageSemantic::Response; + } + else if (value->content.value == L"Event") + { + semantic = ChannelPackageSemantic::Event; + } + } + else if (field->name.value == L"id") + { + auto value = field->value.Cast(); + CHECK_ERROR(value, ERROR_MESSAGE_PREFIX L"The id field should be a number."); + + id = wtoi(value->content.value); + } + else if (field->name.value == L"name") + { + auto value = field->value.Cast(); + CHECK_ERROR(value, ERROR_MESSAGE_PREFIX L"The name field should be a string."); + + name = value->content.value; + } + else if (field->name.value == L"arguments") + { + arguments = field->value; + } + } +#undef ERROR_MESSAGE_PREFIX + } + + void ChannelPackageSemanticUnpack(Ptr package, ChannelPackageSemantic& semantic, vint& id, WString& name) + { + Ptr arguments; + JsonChannelUnpack(package, semantic, id, name, arguments); + } + +/*********************************************************************** +GuiRemoteProtocolFromJsonChannel +***********************************************************************/ + + void GuiRemoteProtocolFromJsonChannel::OnReceive(const Ptr& package) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::channeling::GuiRemoteProtocolFromJsonChannel::OnReceive(const Ptr&)#" + + auto semantic = ChannelPackageSemantic::Unknown; + vint id = -1; + WString name; + Ptr jsonArguments; + JsonChannelUnpack(package, semantic, id, name, jsonArguments); + +#define EVENT_NOREQ(NAME, REQUEST)\ + if (name == L ## #NAME)\ + {\ + events->On ## NAME();\ + } else\ + +#define EVENT_REQ(NAME, REQUEST)\ + if (name == L ## #NAME)\ + {\ + REQUEST arguments;\ + ConvertJsonToCustomType(jsonArguments, arguments);\ + events->On ## NAME(arguments);\ + } else\ + +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST) + if (semantic == ChannelPackageSemantic::Event) + { + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) + { + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unrecognized event name"); + } + } + else +#undef EVENT_HANDLER +#undef EVENT_REQ +#undef EVENT_NOREQ + +#define MESSAGE_NORES(NAME, RESPONSE) +#define MESSAGE_RES(NAME, RESPONSE)\ + if (name == L ## #NAME)\ + {\ + RESPONSE arguments;\ + ConvertJsonToCustomType(jsonArguments, arguments);\ + events->Respond ## NAME(id, arguments);\ + } else\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) + if (semantic == ChannelPackageSemantic::Response) + { + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) + { + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unrecognized response name"); + } + } else +#undef MESSAGE_HANDLER +#undef MESSAGE_RES +#undef MESSAGE_NORES + + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unrecognized category name"); +#undef ERROR_MESSAGE_PREFIX + } + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE)\ + void GuiRemoteProtocolFromJsonChannel::Request ## NAME()\ + {\ + Ptr package;\ + JsonChannelPack(ChannelPackageSemantic::Message, -1, WString::Unmanaged(L ## #NAME), {}, package);\ + channel->Write(package);\ + }\ + +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE)\ + void GuiRemoteProtocolFromJsonChannel::Request ## NAME(vint id)\ + {\ + Ptr package;\ + JsonChannelPack(ChannelPackageSemantic::Request, id, WString::Unmanaged(L ## #NAME), {}, package);\ + channel->Write(package);\ + }\ + +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE)\ + void GuiRemoteProtocolFromJsonChannel::Request ## NAME(const REQUEST& arguments)\ + {\ + Ptr package;\ + JsonChannelPack(ChannelPackageSemantic::Message, -1, WString::Unmanaged(L ## #NAME), ConvertCustomTypeToJson(arguments), package);\ + channel->Write(package);\ + }\ + +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE)\ + void GuiRemoteProtocolFromJsonChannel::Request ## NAME(vint id, const REQUEST& arguments)\ + {\ + Ptr package;\ + JsonChannelPack(ChannelPackageSemantic::Request, id, WString::Unmanaged(L ## #NAME), ConvertCustomTypeToJson(arguments), package);\ + channel->Write(package);\ + }\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES + + GuiRemoteProtocolFromJsonChannel::GuiRemoteProtocolFromJsonChannel(IJsonChannel* _channel) + : channel(_channel) + { + } + + GuiRemoteProtocolFromJsonChannel::~GuiRemoteProtocolFromJsonChannel() + { + } + + void GuiRemoteProtocolFromJsonChannel::Initialize(IGuiRemoteProtocolEvents* _events) + { + events = _events; + channel->Initialize(this); + } + + WString GuiRemoteProtocolFromJsonChannel::GetExecutablePath() + { + return channel->GetExecutablePath(); + } + + void GuiRemoteProtocolFromJsonChannel::Submit(bool& disconnected) + { + channel->Submit(disconnected); + } + + void GuiRemoteProtocolFromJsonChannel::ProcessRemoteEvents() + { + channel->ProcessRemoteEvents(); + } + +/*********************************************************************** +GuiRemoteJsonChannelFromProtocol +***********************************************************************/ + +#define EVENT_NOREQ(NAME, REQUEST)\ + void GuiRemoteJsonChannelFromProtocol::On ## NAME()\ + {\ + Ptr package;\ + JsonChannelPack(ChannelPackageSemantic::Event, -1, WString::Unmanaged(L ## #NAME), {}, package);\ + receiver->OnReceive(package);\ + }\ + +#define EVENT_REQ(NAME, REQUEST)\ + void GuiRemoteJsonChannelFromProtocol::On ## NAME(const REQUEST& arguments)\ + {\ + Ptr package;\ + JsonChannelPack(ChannelPackageSemantic::Event, -1, WString::Unmanaged(L ## #NAME), ConvertCustomTypeToJson(arguments), package);\ + receiver->OnReceive(package);\ + }\ + +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_REQ +#undef EVENT_NOREQ + +#define MESSAGE_NORES(NAME, RESPONSE) +#define MESSAGE_RES(NAME, RESPONSE)\ + void GuiRemoteJsonChannelFromProtocol::Respond ## NAME(vint id, const RESPONSE& arguments)\ + {\ + Ptr package;\ + JsonChannelPack(ChannelPackageSemantic::Response, id, WString::Unmanaged(L ## #NAME), ConvertCustomTypeToJson(arguments), package);\ + receiver->OnReceive(package);\ + }\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_RES +#undef MESSAGE_NORES + + GuiRemoteJsonChannelFromProtocol::GuiRemoteJsonChannelFromProtocol(IGuiRemoteProtocol* _protocol) + : protocol(_protocol) + { + } + + GuiRemoteJsonChannelFromProtocol::~GuiRemoteJsonChannelFromProtocol() + { + } + + void GuiRemoteJsonChannelFromProtocol::Initialize(IJsonChannelReceiver* _receiver) + { + receiver = _receiver; + protocol->Initialize(this); + } + + IJsonChannelReceiver* GuiRemoteJsonChannelFromProtocol::GetReceiver() + { + return receiver; + } + + void GuiRemoteJsonChannelFromProtocol::Write(const Ptr& package) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::channeling::GuiRemoteJsonChannelFromProtocol::Write(const Ptr&)#" + + auto semantic = ChannelPackageSemantic::Unknown; + vint id = -1; + WString name; + Ptr jsonArguments; + JsonChannelUnpack(package, semantic, id, name, jsonArguments); + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE)\ + if (name == L ## #NAME)\ + {\ + protocol->Request ## NAME();\ + } else\ + +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE)\ + if (name == L ## #NAME)\ + {\ + protocol->Request ## NAME(id);\ + } else\ + +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE)\ + if (name == L ## #NAME)\ + {\ + REQUEST arguments;\ + ConvertJsonToCustomType(jsonArguments, arguments);\ + protocol->Request ## NAME(arguments);\ + } else\ + +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE)\ + if (name == L ## #NAME)\ + {\ + REQUEST arguments;\ + ConvertJsonToCustomType(jsonArguments, arguments);\ + protocol->Request ## NAME(id, arguments);\ + } else\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) + { + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unrecognized request name"); + } +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES + +#undef ERROR_MESSAGE_PREFIX + } + + WString GuiRemoteJsonChannelFromProtocol::GetExecutablePath() + { + return protocol->GetExecutablePath(); + } + + void GuiRemoteJsonChannelFromProtocol::Submit(bool& disconnected) + { + protocol->Submit(disconnected); + } + + void GuiRemoteJsonChannelFromProtocol::ProcessRemoteEvents() + { + protocol->ProcessRemoteEvents(); + } + +/*********************************************************************** +JsonToStringSerializer +***********************************************************************/ + + void JsonToStringSerializer::Serialize(Ptr parser, const SourceType& source, DestType& dest) + { + glr::json::JsonFormatting formatting; + formatting.spaceAfterColon = false; + formatting.spaceAfterComma = false; + formatting.crlf = false; + formatting.compact = true; + dest = glr::json::JsonToString(source, formatting); + } + + void JsonToStringSerializer::Deserialize(Ptr parser, const DestType& source, SourceType& dest) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::channeling::GuiRemoteJsonChannelFromProtocol::Write(const Ptr&)#" + auto value = glr::json::JsonParse(source, *parser.Obj()); + dest = value.Cast(); + CHECK_ERROR(dest, ERROR_MESSAGE_PREFIX L"JSON parssing between the channel should be JsonObject."); +#undef ERROR_MESSAGE_PREFIX + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_DOMDIFF.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol +{ + +/*********************************************************************** +GuiRemoteEventDomDiffConverter +***********************************************************************/ + + GuiRemoteEventDomDiffConverter::GuiRemoteEventDomDiffConverter() + { + } + + GuiRemoteEventDomDiffConverter::~GuiRemoteEventDomDiffConverter() + { + } + + void GuiRemoteEventDomDiffConverter::OnControllerConnect() + { + lastDom = {}; + TBase::OnControllerConnect(); + } + +/*********************************************************************** +GuiRemoteProtocolDomDiffConverter +***********************************************************************/ + + GuiRemoteProtocolDomDiffConverter::GuiRemoteProtocolDomDiffConverter(IGuiRemoteProtocol* _protocol) + : TBase(_protocol) + { + } + + GuiRemoteProtocolDomDiffConverter::~GuiRemoteProtocolDomDiffConverter() + { + } + + void GuiRemoteProtocolDomDiffConverter::RequestRendererBeginRendering(const remoteprotocol::ElementBeginRendering& arguments) + { + renderingDomBuilder.RequestRendererBeginRendering(); + TBase::RequestRendererBeginRendering(arguments); + } + + void GuiRemoteProtocolDomDiffConverter::RequestRendererEndRendering(vint id) + { + auto dom = renderingDomBuilder.RequestRendererEndRendering(); + DomIndex domIndex; + BuildDomIndex(dom, domIndex); + + if (eventCombinator.lastDom) + { + RenderingDom_DiffsInOrder diffs; + DiffDom(eventCombinator.lastDom, eventCombinator.lastDomIndex, dom, domIndex, diffs); + targetProtocol->RequestRendererRenderDomDiff(diffs); + } + else + { + targetProtocol->RequestRendererRenderDom(dom); + } + + eventCombinator.lastDom = dom; + eventCombinator.lastDomIndex = std::move(domIndex); + TBase::RequestRendererEndRendering(id); + } + + void GuiRemoteProtocolDomDiffConverter::RequestRendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments) + { + renderingDomBuilder.RequestRendererBeginBoundary(arguments); + } + + void GuiRemoteProtocolDomDiffConverter::RequestRendererEndBoundary() + { + renderingDomBuilder.RequestRendererEndBoundary(); + } + + void GuiRemoteProtocolDomDiffConverter::RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments) + { + renderingDomBuilder.RequestRendererRenderElement(arguments); + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_FILTER.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol::repeatfiltering +{ + +/*********************************************************************** +GuiRemoteEventFilter +***********************************************************************/ + + GuiRemoteEventFilter::GuiRemoteEventFilter() + { + } + + GuiRemoteEventFilter::~GuiRemoteEventFilter() + { + } + + void GuiRemoteEventFilter::ProcessResponses() + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::repeatfiltering::GuiRemoteProtocolFilter::ProcessResponses()#" + for (auto&& response : filteredResponses) + { +#define MESSAGE_NORES(NAME, RESPONSE) +#define MESSAGE_RES(NAME, RESPONSE)\ + case FilteredResponseNames::NAME:\ + targetEvents->Respond ## NAME(response.id, response.arguments.Get());\ + break;\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) + switch (response.name) + { + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) + default: + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unrecognized response."); + } +#undef MESSAGE_HANDLER +#undef MESSAGE_RES +#undef MESSAGE_NORES + } + + CHECK_ERROR(responseIds.Count() == 0, ERROR_MESSAGE_PREFIX L"Messages sending to IGuiRemoteProtocol should be all responded."); + filteredResponses.Clear(); +#undef ERROR_MESSAGE_PREFIX + } + + void GuiRemoteEventFilter::ProcessEvents() + { +#define EVENT_NODROP(NAME) +#define EVENT_DROPREP(NAME) lastDropRepeatEvent ## NAME = -1; +#define EVENT_DROPCON(NAME) lastDropConsecutiveEvent ## NAME = -1; +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## DROPTAG(NAME) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_DROPCON +#undef EVENT_DROPREP +#undef EVENT_NODROP + + collections::List events(std::move(filteredEvents)); + + for (auto&& event : events) + { + if (event.dropped) + { + continue; + } + +#define EVENT_NOREQ(NAME, REQUEST)\ + case FilteredEventNames::NAME:\ + targetEvents->On ## NAME();\ + break;\ + +#define EVENT_REQ(NAME, REQUEST)\ + case FilteredEventNames::NAME:\ + targetEvents->On ## NAME(event.arguments.Get());\ + break;\ + +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST) + switch (event.name) + { + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) + default: + CHECK_FAIL(L"vl::presentation::remoteprotocol::GuiRemoteEventFilter::ProcessEvents()#Unrecognized event."); + } +#undef EVENT_HANDLER +#undef EVENT_REQ +#undef EVENT_NOREQ + } + } + + // responses + +#define MESSAGE_NORES(NAME, RESPONSE) +#define MESSAGE_RES(NAME, RESPONSE)\ + void GuiRemoteEventFilter::Respond ## NAME(vint id, const RESPONSE& arguments)\ + {\ + CHECK_ERROR(\ + responseIds[id] == FilteredResponseNames::NAME,\ + L"vl::presentation::remoteprotocol::GuiRemoteEventFilter::"\ + L"Respond" L ## #NAME L"()#"\ + L"Messages sending to IGuiRemoteProtocol should be responded by calling the correct function.");\ + responseIds.Remove(id);\ + FilteredResponse response;\ + response.id = id;\ + response.name = FilteredResponseNames::NAME;\ + response.arguments = arguments;\ + filteredResponses.Add(response);\ + }\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_RES +#undef MESSAGE_NORES + + // events + +#define EVENT_NODROP(NAME) + +#define EVENT_DROPREP(NAME)\ + if (lastDropRepeatEvent ## NAME != -1)\ + {\ + filteredEvents[lastDropRepeatEvent ## NAME].dropped = true;\ + }\ + lastDropRepeatEvent ## NAME = filteredEvents.Count() - 1\ + +#define EVENT_DROPCON(NAME)\ + if (lastDropConsecutiveEvent ## NAME != -1 && lastDropConsecutiveEvent ## NAME == filteredEvents.Count() - 1)\ + {\ + filteredEvents[lastDropConsecutiveEvent ## NAME].dropped = true;\ + }\ + lastDropConsecutiveEvent ## NAME = filteredEvents.Count() - 1\ + +#define EVENT_NOREQ(NAME, REQUEST, DROPTAG)\ + void GuiRemoteEventFilter::On ## NAME()\ + {\ + if (submitting)\ + {\ + EVENT_ ## DROPTAG(NAME);\ + FilteredEvent event;\ + event.name = FilteredEventNames::NAME;\ + filteredEvents.Add(event);\ + }\ + else\ + {\ + targetEvents->On ## NAME();\ + }\ + }\ + +#define EVENT_REQ(NAME, REQUEST, DROPTAG)\ + void GuiRemoteEventFilter::On ## NAME(const REQUEST& arguments)\ + {\ + if (submitting)\ + {\ + EVENT_ ## DROPTAG(NAME);\ + FilteredEvent event;\ + event.name = FilteredEventNames::NAME;\ + event.arguments = arguments;\ + filteredEvents.Add(event);\ + }\ + else\ + {\ + targetEvents->On ## NAME(arguments);\ + }\ + }\ + +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST, DROPTAG) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_REQ +#undef EVENT_NOREQ +#undef EVENT_DROPCON +#undef EVENT_DROPREP +#undef EVENT_NOREP + +/*********************************************************************** +GuiRemoteProtocolFilter +***********************************************************************/ + + void GuiRemoteProtocolFilter::ProcessRequests() + { +#define MESSAGE_NODROP(NAME) +#define MESSAGE_DROPREP(NAME) lastDropRepeatRequest ## NAME = -1; +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG) MESSAGE_ ## DROPTAG(NAME) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_DROPREP +#undef MESSAGE_NODROP + + for (auto&& request : filteredRequests) + { + CHECK_ERROR(\ + !request.dropped || request.id == -1,\ + L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::ProcessRequests()#"\ + L"Messages with id cannot be dropped.");\ + if (request.dropped) + { + continue; + } + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE)\ + case FilteredRequestNames::NAME:\ + targetProtocol->Request ## NAME();\ + break;\ + +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE)\ + case FilteredRequestNames::NAME:\ + targetProtocol->Request ## NAME(request.id);\ + break;\ + +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE)\ + case FilteredRequestNames::NAME:\ + targetProtocol->Request ## NAME(request.arguments.Get());\ + break;\ + +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE)\ + case FilteredRequestNames::NAME:\ + targetProtocol->Request ## NAME(request.id, request.arguments.Get());\ + break;\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE) + switch (request.name) + { + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) + default: + CHECK_FAIL(L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::ProcessRequests()#Unrecognized request."); + } +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES + } + + filteredRequests.Clear(); + } + + GuiRemoteProtocolFilter::GuiRemoteProtocolFilter(IGuiRemoteProtocol* _protocol) + : GuiRemoteProtocolCombinator(_protocol) + { + } + + GuiRemoteProtocolFilter::~GuiRemoteProtocolFilter() + { + } + + // messages + +#define MESSAGE_NODROP(NAME) + +#define MESSAGE_DROPREP(NAME)\ + if (lastDropRepeatRequest ## NAME != -1)\ + {\ + filteredRequests[lastDropRepeatRequest ## NAME].dropped = true;\ + }\ + lastDropRepeatRequest ## NAME = filteredRequests.Count()\ + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilter::Request ## NAME()\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + FilteredRequest request;\ + request.name = FilteredRequestNames::NAME;\ + filteredRequests.Add(request);\ + }\ + +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilter::Request ## NAME(vint id)\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + CHECK_ERROR(\ + lastRequestId < id,\ + L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::"\ + L"Request" L ## #NAME L"()#"\ + L"Id of a message sending to IGuiRemoteProtocol should be increasing.");\ + lastRequestId = id;\ + FilteredRequest request;\ + request.id = id;\ + request.name = FilteredRequestNames::NAME;\ + filteredRequests.Add(request);\ + eventCombinator.responseIds.Add(id, FilteredResponseNames::NAME);\ + }\ + +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilter::Request ## NAME(const REQUEST& arguments)\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + FilteredRequest request;\ + request.name = FilteredRequestNames::NAME;\ + request.arguments = arguments;\ + filteredRequests.Add(request);\ + }\ + +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilter::Request ## NAME(vint id, const REQUEST& arguments)\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + CHECK_ERROR(\ + lastRequestId < id,\ + L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::"\ + L"Request" L ## #NAME L"()#"\ + L"Id of a message sending to IGuiRemoteProtocol should be increasing.");\ + lastRequestId = id;\ + FilteredRequest request;\ + request.id = id;\ + request.name = FilteredRequestNames::NAME;\ + request.arguments = arguments;\ + filteredRequests.Add(request);\ + eventCombinator.responseIds.Add(id, FilteredResponseNames::NAME);\ + }\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE, DROPTAG) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES +#undef MESSAGE_DROPREP +#undef MESSAGE_NODROP + + // protocol + + void GuiRemoteProtocolFilter::Initialize(IGuiRemoteProtocolEvents* _events) + { + if (auto verifierProtocol = dynamic_cast(targetProtocol)) + { + verifierProtocol->targetProtocol->Initialize(&eventCombinator); + eventCombinator.targetEvents = &verifierProtocol->eventCombinator; + verifierProtocol->eventCombinator.targetEvents = _events; + } + else + { + GuiRemoteProtocolCombinator::Initialize(_events); + } + } + + void GuiRemoteProtocolFilter::Submit(bool& disconnected) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::repeatfiltering::GuiRemoteProtocolFilter::Submit()#" + CHECK_ERROR(!eventCombinator.submitting, ERROR_MESSAGE_PREFIX L"This function is not allowed to be called recursively."); + eventCombinator.submitting = true; + ProcessRequests(); + GuiRemoteProtocolCombinator::Submit(disconnected); + if (disconnected) + { + eventCombinator.responseIds.Clear(); + } + else + { + eventCombinator.ProcessResponses(); + } + eventCombinator.submitting = false; + eventCombinator.ProcessEvents(); +#undef ERROR_MESSAGE_PREFIX + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_FILTERVERIFIER.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol::repeatfiltering +{ +/*********************************************************************** +GuiRemoteEventFilterVerifier +***********************************************************************/ + + GuiRemoteEventFilterVerifier::GuiRemoteEventFilterVerifier() + { + } + + GuiRemoteEventFilterVerifier::~GuiRemoteEventFilterVerifier() + { + } + + void GuiRemoteEventFilterVerifier::ClearDropRepeatMasks() + { +#define EVENT_NODROP(NAME) +#define EVENT_DROPREP(NAME) lastDropRepeatEvent ## NAME = false; +#define EVENT_DROPCON(NAME) +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## DROPTAG(NAME) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_DROPCON +#undef EVENT_DROPREP +#undef EVENT_NODROP + } + + void GuiRemoteEventFilterVerifier::ClearDropConsecutiveMasks() + { +#define EVENT_NODROP(NAME) +#define EVENT_DROPREP(NAME) +#define EVENT_DROPCON(NAME) lastDropConsecutiveEvent ## NAME = false; +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## DROPTAG(NAME) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_DROPCON +#undef EVENT_DROPREP +#undef EVENT_NODROP + } + + // responses + +#define MESSAGE_NORES(NAME, RESPONSE) +#define MESSAGE_RES(NAME, RESPONSE)\ + void GuiRemoteEventFilterVerifier::Respond ## NAME(vint id, const RESPONSE& arguments)\ + {\ + targetEvents->Respond ## NAME(id, arguments);\ + }\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_RES +#undef MESSAGE_NORES + + // events + +#define EVENT_NODROP(NAME) + +#define EVENT_DROPREP(NAME)\ + CHECK_ERROR(!lastDropRepeatEvent ## NAME, L"vl::presentation::remoteprotocol::GuiRemoteEventFilterVerifier::On" L ## #NAME L"(...)#[@DropRepeat] event repeated.");\ + lastDropRepeatEvent ## NAME = true;\ + +#define EVENT_DROPCON(NAME)\ + CHECK_ERROR(!lastDropConsecutiveEvent ## NAME, L"vl::presentation::remoteprotocol::GuiRemoteEventFilterVerifier::On" L ## #NAME L"(...)#[@DropConsecutive] event repeated.");\ + ClearDropConsecutiveMasks();\ + lastDropConsecutiveEvent ## NAME = true;\ + +#define EVENT_NOREQ(NAME, REQUEST, DROPTAG)\ + void GuiRemoteEventFilterVerifier::On ## NAME()\ + {\ + if (submitting)\ + {\ + EVENT_ ## DROPTAG(NAME);\ + targetEvents->On ## NAME();\ + }\ + else\ + {\ + targetEvents->On ## NAME();\ + }\ + }\ + +#define EVENT_REQ(NAME, REQUEST, DROPTAG)\ + void GuiRemoteEventFilterVerifier::On ## NAME(const REQUEST& arguments)\ + {\ + if (submitting)\ + {\ + EVENT_ ## DROPTAG(NAME);\ + targetEvents->On ## NAME(arguments);\ + }\ + else\ + {\ + targetEvents->On ## NAME(arguments);\ + }\ + }\ + +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST, DROPTAG) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_REQ +#undef EVENT_NOREQ +#undef EVENT_DROPCON +#undef EVENT_DROPREP +#undef EVENT_NOREP + +/*********************************************************************** +GuiRemoteProtocolFilterVerifier +***********************************************************************/ + + void GuiRemoteProtocolFilterVerifier::ClearDropRepeatMasks() + { +#define MESSAGE_NODROP(NAME) +#define MESSAGE_DROPREP(NAME) lastDropRepeatRequest ## NAME = false; +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG) MESSAGE_ ## DROPTAG(NAME) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_DROPREP +#undef MESSAGE_NODROP + } + + GuiRemoteProtocolFilterVerifier::GuiRemoteProtocolFilterVerifier(IGuiRemoteProtocol* _protocol) + : GuiRemoteProtocolCombinator(_protocol) + { + } + + GuiRemoteProtocolFilterVerifier::~GuiRemoteProtocolFilterVerifier() + { + } + + // messages + +#define MESSAGE_NODROP(NAME) + +#define MESSAGE_DROPREP(NAME)\ + CHECK_ERROR(!lastDropRepeatRequest ## NAME, L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilterVerifier::Request" L ## #NAME L"(...)#[@DropRepeat] message repeated.");\ + lastDropRepeatRequest ## NAME = true;\ + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilterVerifier::Request ## NAME()\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + targetProtocol->Request ## NAME();\ + }\ + +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilterVerifier::Request ## NAME(vint id)\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + targetProtocol->Request ## NAME(id);\ + }\ + +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilterVerifier::Request ## NAME(const REQUEST& arguments)\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + targetProtocol->Request ## NAME(arguments);\ + }\ + +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ + void GuiRemoteProtocolFilterVerifier::Request ## NAME(vint id, const REQUEST& arguments)\ + {\ + MESSAGE_ ## DROPTAG(NAME);\ + targetProtocol->Request ## NAME(id, arguments);\ + }\ + +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE, DROPTAG) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES +#undef MESSAGE_DROPREP +#undef MESSAGE_NODROP + + // protocol + + void GuiRemoteProtocolFilterVerifier::Submit(bool& disconnected) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::repeatfiltering::GuiRemoteProtocolFilterVerifier::Submit()#" + CHECK_ERROR(!eventCombinator.submitting, ERROR_MESSAGE_PREFIX L"This function is not allowed to be called recursively."); + eventCombinator.submitting = true; + GuiRemoteProtocolCombinator::Submit(disconnected); + ClearDropRepeatMasks(); + eventCombinator.ClearDropRepeatMasks(); + eventCombinator.ClearDropConsecutiveMasks(); + eventCombinator.submitting = false; +#undef ERROR_MESSAGE_PREFIX + } +} + /*********************************************************************** .\PLATFORMPROVIDERS\REMOTE\GUIREMOTEWINDOW.CPP ***********************************************************************/ @@ -40277,9 +41551,11 @@ GuiRemoteWindow void GuiRemoteWindow::RequestGetBounds() { - sizingConfigInvalidated = false; vint idGetBounds = remoteMessages.RequestWindowGetBounds(); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + if (disconnected) return; + sizingConfigInvalidated = false; OnWindowBoundsUpdated(remoteMessages.RetrieveWindowGetBounds(idGetBounds)); } @@ -40318,7 +41594,9 @@ GuiRemoteWindow windowShowing.activate = activate; windowShowing.sizeState = sizeState; remoteMessages.RequestWindowNotifyShow(windowShowing); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded remoteWindowSizingConfig.sizeState = sizeState; Opened(); @@ -40342,8 +41620,15 @@ GuiRemoteWindow (events) } sizingConfigInvalidated = true; + remoteMessages.RequestWindowNotifySetBounds(remoteWindowSizingConfig.bounds); RequestGetBounds(); + // TODO: + // This is a workaround to call GuiWindow::UpdateCustomFramePadding + // Refactor to make it more elegant. + for (auto l : listeners) l->DpiChanged(true); + for (auto l : listeners) l->DpiChanged(false); + if (remote->applicationRunning) { remoteMessages.RequestWindowNotifySetTitle(styleTitle); @@ -40365,7 +41650,9 @@ GuiRemoteWindow (events) { remoteMessages.RequestIOReleaseCapture(); } - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded } } @@ -40486,6 +41773,17 @@ GuiRemoteWindow (INativeWindow) { if (remoteWindowSizingConfig.bounds != bounds) { + auto x1 = remoteWindowSizingConfig.clientBounds.x1 - remoteWindowSizingConfig.bounds.x1; + auto y1 = remoteWindowSizingConfig.clientBounds.y1 - remoteWindowSizingConfig.bounds.y1; + auto x2 = remoteWindowSizingConfig.clientBounds.x2 - remoteWindowSizingConfig.bounds.x2; + auto y2 = remoteWindowSizingConfig.clientBounds.y2 - remoteWindowSizingConfig.bounds.y2; + remoteWindowSizingConfig.bounds = bounds; + remoteWindowSizingConfig.clientBounds = { + x1 + remoteWindowSizingConfig.bounds.x1, + y1 + remoteWindowSizingConfig.bounds.y1, + x2 + remoteWindowSizingConfig.bounds.x2, + y2 + remoteWindowSizingConfig.bounds.y2 + }; remoteMessages.RequestWindowNotifySetBounds(bounds); sizingConfigInvalidated = true; } @@ -40501,6 +41799,17 @@ GuiRemoteWindow (INativeWindow) { if (remoteWindowSizingConfig.clientBounds.GetSize() != size) { + auto x1 = remoteWindowSizingConfig.bounds.x1 - remoteWindowSizingConfig.clientBounds.x1; + auto y1 = remoteWindowSizingConfig.bounds.y1 - remoteWindowSizingConfig.clientBounds.y1; + auto x2 = remoteWindowSizingConfig.bounds.x2 - remoteWindowSizingConfig.clientBounds.x2; + auto y2 = remoteWindowSizingConfig.bounds.y2 - remoteWindowSizingConfig.clientBounds.y2; + remoteWindowSizingConfig.clientBounds = { remoteWindowSizingConfig.clientBounds.LeftTop(),size }; + remoteWindowSizingConfig.bounds = { + x1 + remoteWindowSizingConfig.clientBounds.x1, + y1 + remoteWindowSizingConfig.clientBounds.y1, + x2 + remoteWindowSizingConfig.clientBounds.x2, + y2 + remoteWindowSizingConfig.clientBounds.y2 + }; remoteMessages.RequestWindowNotifySetClientSize(size); sizingConfigInvalidated = true; } @@ -40725,7 +42034,9 @@ GuiRemoteWindow (INativeWindow) { statusCapturing = true; remoteMessages.RequestIORequireCapture(); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded } return true; } @@ -40736,7 +42047,9 @@ GuiRemoteWindow (INativeWindow) { statusCapturing = false; remoteMessages.RequestIOReleaseCapture(); - remoteMessages.Submit(); + bool disconnected = false; + remoteMessages.Submit(disconnected); + // there is no result from this request, assuming succeeded } return true; } @@ -40854,6 +42167,605 @@ GuiRemoteWindow (INativeWindow) #undef SET_REMOTE_WINDOW_STYLE } +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\PROTOCOL\FRAMEOPERATIONS\GUIREMOTEPROTOCOLSCHEMA_BUILDFRAME.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol +{ + vint RenderingDomBuilder::GetCurrentBoundary() + { + if (domBoundaries.Count() > 0) + { + return domBoundaries[domBoundaries.Count() - 1]; + } + else + { + return 0; + } + } + + vint RenderingDomBuilder::Push(RenderingResultRef ref) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::RenderingDomBuilder::Push(RenderingResultRef)#" + CHECK_ERROR(ref, ERROR_MESSAGE_PREFIX L"Cannot push a null dom object."); + vint index = domStack.Add(ref); + if (!domCurrent->children) domCurrent->children = Ptr(new RenderingResultRefList); + domCurrent->children->Add(ref); + domCurrent = ref; + return index; +#undef ERROR_MESSAGE_PREFIX + } + + void RenderingDomBuilder::PopTo(vint index) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::RenderingDomBuilder::PopTo(vint)#" + if (index == domStack.Count() - 1) return; + CHECK_ERROR(0 <= index && index < domStack.Count(), ERROR_MESSAGE_PREFIX L"Cannot pop to an invalid position."); + CHECK_ERROR(index >= GetCurrentBoundary(), ERROR_MESSAGE_PREFIX L"Cannot pop across a boundary."); + while (domStack.Count() - 1 > index) + { + domStack.RemoveAt(domStack.Count() - 1); + } + domCurrent = domStack[index]; +#undef ERROR_MESSAGE_PREFIX + } + + void RenderingDomBuilder::Pop() + { + PopTo(domStack.Count() - 2); + } + + void RenderingDomBuilder::PopBoundary() + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::RenderingDomBuilder::PopBoundary()#" + CHECK_ERROR(domBoundaries.Count() > 0, ERROR_MESSAGE_PREFIX L"Cannot pop a boundary when none is in the stack."); + auto boundaryIndex = domBoundaries.Count() - 1; + auto boundary = domBoundaries[boundaryIndex]; + domBoundaries.RemoveAt(boundaryIndex); + PopTo(boundary - 1); +#undef ERROR_MESSAGE_PREFIX + } + + + template + void RenderingDomBuilder::PrepareParentFromCommand(Rect commandBounds, Rect commandValidArea, vint newDomId, TCallback&& calculateValidAreaFromDom) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::RenderingDomBuilder::PrepareParentFromCommand(Rect, Rect, vint, auto&&)#" + vint min = GetCurrentBoundary(); + bool found = false; + if (commandValidArea.Contains(commandBounds)) + { + // if the command is not clipped + for (vint i = domStack.Count() - 1; i >= min; i--) + { + if (domStack[i]->content.validArea.Contains(commandBounds) || i == 0) + { + // find the deepest node that could contain the command + PopTo(i); + found = true; + break; + } + } + } + else + { + // otherwise, a parent node causing such clipping should be found or created + for (vint i = domStack.Count() - 1; i >= min; i--) + { + auto domValidArea = calculateValidAreaFromDom(domStack[i]); + if (domValidArea == commandValidArea) + { + // if there is a node who clips command's bound to its valid area + // that is the parent node of the command + PopTo(i); + found = true; + break; + } + else if (domValidArea.Contains(commandValidArea) || i == 0) + { + // otherwise find a deepest node who could visually contain the command + // create a virtual node to satisfy the clipper + PopTo(i); + auto parent = Ptr(new RenderingDom); + parent->id = newDomId; + parent->content.bounds = commandValidArea; + parent->content.validArea = commandValidArea; + Push(parent); + found = true; + break; + } + } + } + + // if the new boundary could not fit in the current boundary + // there must be something wrong + CHECK_ERROR(found, ERROR_MESSAGE_PREFIX L"Incorrect valid area of dom."); +#undef ERROR_MESSAGE_PREFIX + } + + void RenderingDomBuilder::RequestRendererBeginRendering() + { + domStack.Clear(); + domBoundaries.Clear(); + domRoot = Ptr(new RenderingDom); + domRoot->id = -1; + domCurrent = domRoot; + domStack.Add(domRoot); + } + + void RenderingDomBuilder::RequestRendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments) + { + // a new boundary should be a new node covering existing nodes + // the valid area of boundary is clipped by its bounds + // so the valid area to compare from its potential parent dom needs to clipped by its bounds + PrepareParentFromCommand( + arguments.bounds, + arguments.areaClippedBySelf, + (arguments.id << 2) + 3, + [&](auto&& dom) { return dom->content.validArea.Intersect(arguments.bounds); } + ); + + auto dom = Ptr(new RenderingDom); + dom->id = (arguments.id << 2) + 2; + dom->content.hitTestResult = arguments.hitTestResult; + dom->content.cursor = arguments.cursor; + dom->content.bounds = arguments.bounds; + dom->content.validArea = arguments.areaClippedBySelf; + domBoundaries.Add(Push(dom)); + } + + void RenderingDomBuilder::RequestRendererEndBoundary() + { + PopBoundary(); + } + + void RenderingDomBuilder::RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments) + { + // a new element should be a new node covering existing nodes + // the valid area of boundary is clipped by its parent + // so the valid area to compare from its potential parent dom is its valid area + PrepareParentFromCommand( + arguments.bounds, + arguments.areaClippedByParent, + (arguments.id << 2) + 1, + [&](auto&& dom) { return dom->content.validArea; } + ); + + auto dom = Ptr(new RenderingDom); + dom->id = (arguments.id << 2) + 0; + dom->content.element = arguments.id; + dom->content.bounds = arguments.bounds; + dom->content.validArea = arguments.bounds.Intersect(arguments.areaClippedByParent); + Push(dom); + } + + Ptr RenderingDomBuilder::RequestRendererEndRendering() + { + return domRoot; + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\PROTOCOL\FRAMEOPERATIONS\GUIREMOTEPROTOCOLSCHEMA_COPYDOM.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol +{ + using namespace collections; + + Ptr CopyDom(Ptr root) + { + auto newRoot = Ptr(new RenderingDom); + newRoot->id = root->id; + newRoot->content = root->content; + if (root->children) + { + newRoot->children = Ptr(new List>); + for (auto child : *root->children.Obj()) + { + newRoot->children->Add(CopyDom(child)); + } + } + return newRoot; + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\PROTOCOL\FRAMEOPERATIONS\GUIREMOTEPROTOCOLSCHEMA_DIFFFRAME.CPP +***********************************************************************/ + +namespace vl::presentation::remoteprotocol +{ + using namespace collections; + + vint CountDomIndex(Ptr root) + { + vint counter = 1; + if (root->children) + { + for (auto child : *root->children.Obj()) + { + counter += CountDomIndex(child); + } + } + return counter; + } + + void BuildDomIndexInternal(Ptr dom, vint parentId, DomIndex& index, vint& writing) + { + index[writing++] = { dom->id,parentId,dom }; + if (dom->children) + { + for (auto child : *dom->children.Obj()) + { + BuildDomIndexInternal(child, dom->id, index, writing); + } + } + } + + void SortDomIndex(DomIndex& index) + { + if (index.Count() > 0) + { + SortLambda(&index[0], index.Count(), [](const DomIndexItem& a, const DomIndexItem& b) + { + return a.id <=> b.id; + }); + } + } + + void BuildDomIndex(Ptr root, DomIndex& index) + { + vint count = CountDomIndex(root); + vint writing = 0; + index.Resize(count); + if (count > 0) + { + BuildDomIndexInternal(root, -1, index, writing); + SortDomIndex(index); + } + } + + void UpdateDomInplace(Ptr root, DomIndex& index, const RenderingDom_DiffsInOrder& diffs) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::UpdateDomInplace(Ptr, DomIndex&, const RenderingDom_DiffsInOrder&)#" + CHECK_ERROR(root && root->id == -1, ERROR_MESSAGE_PREFIX L"Roots of a DOM must have ID -1."); + + vint createdCount = 0; + + // creating + { + vint readingFrom = 0; + vint readingTo = 0; + + auto markCreated = [&]() + { + auto&& to = diffs.diffsInOrder->Get(readingTo++); + CHECK_ERROR(to.diffType == RenderingDom_DiffType::Created, ERROR_MESSAGE_PREFIX L"Diff of unexisting node must have diffType == Created."); + createdCount++; + }; + + while (diffs.diffsInOrder && readingTo < diffs.diffsInOrder->Count()) + { + if (readingFrom < index.Count()) + { + auto&& from = index[readingFrom]; + auto&& to = diffs.diffsInOrder->Get(readingTo); + if (from.id < to.id) + { + // Nothing happened to this DOM node + readingFrom++; + } + else if (from.id > to.id) + { + markCreated(); + } + else + { + // Modified will be delayed and processed together with Created + readingFrom++; + readingTo++; + CHECK_ERROR(to.diffType != RenderingDom_DiffType::Created, ERROR_MESSAGE_PREFIX L"Diff of existing node must have diffType != Created."); + } + } + else + { + markCreated(); + } + } + } + + { + vint writing = index.Count(); + index.Resize(index.Count() + createdCount); + if (diffs.diffsInOrder) + { + + for (auto&& to : *diffs.diffsInOrder.Obj()) + { + if (to.diffType == RenderingDom_DiffType::Created) + { + // parentId will be filled later + auto dom = Ptr(new RenderingDom); + dom->id = to.id; + index[writing++] = { to.id,-1,dom }; + } + } + } + SortDomIndex(index); + } + + // modifying + { + vint readingFrom = 0; + vint readingTo = 0; + + while (readingFrom < index.Count() && (diffs.diffsInOrder && readingTo < diffs.diffsInOrder->Count())) + { + bool hasFrom = readingFrom < index.Count(); + bool hasTo = diffs.diffsInOrder && readingTo < diffs.diffsInOrder->Count(); + + auto&& from = index[readingFrom]; + auto&& to = diffs.diffsInOrder->Get(readingTo); + if (from.id < to.id) + { + readingFrom++; + } + else if (from.id > to.id) + { + readingTo++; + } + else + { + readingFrom++; + readingTo++; + + if (to.diffType != RenderingDom_DiffType::Deleted) + { + if (to.content) + { + from.dom->content = to.content.Value(); + } + + if (to.children) + { + if (to.children->Count() == 0) + { + from.dom->children = nullptr; + } + else + { + from.dom->children = Ptr(new List>); + for (vint childId : *to.children.Obj()) + { + // Binary search in index for childId + vint start = 0; + vint end = index.Count() - 1; + bool found = false; + while (start <= end) + { + vint mid = (start + end) / 2; + vint midId = index[mid].id; + if (childId < midId) + { + end = mid - 1; + } + else if (childId > midId) + { + start = mid + 1; + } + else + { + // Fill parentId of the new DOM node + index[mid].parentId = from.id; + from.dom->children->Add(index[mid].dom); + found = true; + break; + } + } + CHECK_ERROR(found, ERROR_MESSAGE_PREFIX L"Unknown DOM id in diff."); + } + } + } + } + } + } + } + + // deleting + { + vint readingFrom = 0; + vint readingTo = 0; + List deleteIndices; + + while (diffs.diffsInOrder && readingTo < diffs.diffsInOrder->Count()) + { + if (readingFrom < index.Count()) + { + auto&& from = index[readingFrom]; + auto&& to = diffs.diffsInOrder->Get(readingTo); + if (from.id < to.id) + { + readingFrom++; + } + else if (from.id > to.id) + { + readingTo++; + } + else + { + if (to.diffType == RenderingDom_DiffType::Deleted) + { + deleteIndices.Add(readingFrom); + } + readingFrom++; + readingTo++; + } + } + else + { + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Nodes to be deleted must should appear in the index before modification"); + } + } + + vint reading = 0; + vint writing = 0; + vint testing = 0; + + while (reading < index.Count()) + { + if (testing < deleteIndices.Count() && deleteIndices[testing] == reading) + { + // A node to delete is found, mark and skip + testing++; + reading++; + } + else + { + if (reading != writing) + { + // Compact index by removing deleted entries + index[writing] = index[reading]; + } + reading++; + writing++; + } + } + index.Resize(index.Count() - deleteIndices.Count()); + } +#undef ERROR_MESSAGE_PREFIX + } + + void DiffDom(Ptr domFrom, DomIndex& indexFrom, Ptr domTo, DomIndex& indexTo, RenderingDom_DiffsInOrder& diffs) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::DiffDom(Ptr, DomIndex&, Ptr, DomIndex&, RenderingDom_DiffsInOrder&)#" + CHECK_ERROR(domFrom && domTo && domFrom->id == domTo->id, ERROR_MESSAGE_PREFIX L"Roots of two DOMs tree must have the same ID."); + diffs.diffsInOrder = Ptr(new List); + + vint readingFrom = 0; + vint readingTo = 0; + + auto pushDeleted = [&]() + { + auto&& dom = indexFrom[readingFrom++].dom; + RenderingDom_Diff diff; + + diff.id = dom->id; + diff.diffType = RenderingDom_DiffType::Deleted; + diffs.diffsInOrder->Add(diff); + }; + + auto pushCreated = [&]() + { + auto&& dom = indexTo[readingTo++].dom; + RenderingDom_Diff diff; + diff.id = dom->id; + diff.diffType = RenderingDom_DiffType::Created; + + diff.content = dom->content; + if (dom->children && dom->children->Count() > 0) + { + diff.children = Ptr(new List); + CopyFrom( + *diff.children.Obj(), + From(*dom->children.Obj()) + .Select([](Ptr child) { return child->id; }) + ); + } + diffs.diffsInOrder->Add(diff); + }; + + auto pushModified = [&]() + { + auto&& domFrom = indexFrom[readingFrom++].dom; + auto&& domTo = indexTo[readingTo++].dom; + RenderingDom_Diff diff; + diff.id = domFrom->id; + diff.diffType = RenderingDom_DiffType::Modified; + + if ( + domFrom->content.hitTestResult != domTo->content.hitTestResult || + domFrom->content.cursor != domTo->content.cursor || + domFrom->content.element != domTo->content.element || + domFrom->content.bounds != domTo->content.bounds || + domFrom->content.validArea != domTo->content.validArea + ) + { + diff.content = domTo->content; + } + + bool fromHasChild = domFrom->children && domFrom->children->Count() > 0; + bool toHasChild = domTo->children && domTo->children->Count() > 0; + bool childDifferent = false; + + if (fromHasChild != toHasChild) + { + childDifferent = true; + } + else if (fromHasChild && toHasChild) + { + auto fromIds = From(*domFrom->children.Obj()) + .Select([](Ptr child) { return child->id; }); + auto toIds = From(*domTo->children.Obj()) + .Select([](Ptr child) { return child->id; }); + childDifferent = CompareEnumerable(fromIds, toIds) != 0; + } + + if (childDifferent) + { + diff.children = Ptr(new List); + if (toHasChild) + { + CopyFrom( + *diff.children.Obj(), + From(*domTo->children.Obj()) + .Select([](Ptr child) { return child->id; }) + ); + } + } + + if (diff.content || diff.children) + { + diffs.diffsInOrder->Add(diff); + } + }; + + while (true) + { + if (readingFrom < indexFrom.Count() && readingTo < indexTo.Count()) + { + if (indexFrom[readingFrom].id < indexTo[readingTo].id) + { + pushDeleted(); + } + else if (indexFrom[readingFrom].id > indexTo[readingTo].id) + { + pushCreated(); + } + else + { + pushModified(); + } + } + else if (readingFrom < indexFrom.Count()) + { + pushDeleted(); + } + else if (readingTo < indexTo.Count()) + { + pushCreated(); + } + else + { + break; + } + } + +#undef ERROR_MESSAGE_PREFIX + } +} + /*********************************************************************** .\PLATFORMPROVIDERS\REMOTE\PROTOCOL\GENERATED\GUIREMOTEPROTOCOLSCHEMA.CPP ***********************************************************************/ @@ -41080,6 +42992,21 @@ namespace vl::presentation::remoteprotocol #undef ERROR_MESSAGE_PREFIX } + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom_DiffType>(const ::vl::presentation::remoteprotocol::RenderingDom_DiffType & value) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom_DiffType>(const ::vl::presentation::remoteprotocol::RenderingDom_DiffType&)#" + auto node = Ptr(new glr::json::JsonString); + switch (value) + { + case ::vl::presentation::remoteprotocol::RenderingDom_DiffType::Deleted: node->content.value = WString::Unmanaged(L"Deleted"); break; + case ::vl::presentation::remoteprotocol::RenderingDom_DiffType::Created: node->content.value = WString::Unmanaged(L"Created"); break; + case ::vl::presentation::remoteprotocol::RenderingDom_DiffType::Modified: node->content.value = WString::Unmanaged(L"Modified"); break; + default: CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported enum value."); + } + return node; +#undef ERROR_MESSAGE_PREFIX + } + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::NativeCoordinate>(const ::vl::presentation::NativeCoordinate & value) { auto node = Ptr(new glr::json::JsonObject); @@ -41416,6 +43343,7 @@ namespace vl::presentation::remoteprotocol template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::ElementBoundary>(const ::vl::presentation::remoteprotocol::ElementBoundary & value) { auto node = Ptr(new glr::json::JsonObject); + ConvertCustomTypeToJsonField(node, L"id", value.id); ConvertCustomTypeToJsonField(node, L"hitTestResult", value.hitTestResult); ConvertCustomTypeToJsonField(node, L"cursor", value.cursor); ConvertCustomTypeToJsonField(node, L"bounds", value.bounds); @@ -41449,7 +43377,7 @@ namespace vl::presentation::remoteprotocol return node; } - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom>(const ::vl::presentation::remoteprotocol::RenderingDom & value) + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDomContent>(const ::vl::presentation::remoteprotocol::RenderingDomContent & value) { auto node = Ptr(new glr::json::JsonObject); ConvertCustomTypeToJsonField(node, L"hitTestResult", value.hitTestResult); @@ -41457,44 +43385,47 @@ namespace vl::presentation::remoteprotocol ConvertCustomTypeToJsonField(node, L"element", value.element); ConvertCustomTypeToJsonField(node, L"bounds", value.bounds); ConvertCustomTypeToJsonField(node, L"validArea", value.validArea); + return node; + } + + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom>(const ::vl::presentation::remoteprotocol::RenderingDom & value) + { + auto node = Ptr(new glr::json::JsonObject); + ConvertCustomTypeToJsonField(node, L"id", value.id); + ConvertCustomTypeToJsonField(node, L"content", value.content); ConvertCustomTypeToJsonField(node, L"children", value.children); return node; } - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary>(const ::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary & value) + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom_Diff>(const ::vl::presentation::remoteprotocol::RenderingDom_Diff & value) { auto node = Ptr(new glr::json::JsonObject); - ConvertCustomTypeToJsonField(node, L"boundary", value.boundary); + ConvertCustomTypeToJsonField(node, L"id", value.id); + ConvertCustomTypeToJsonField(node, L"diffType", value.diffType); + ConvertCustomTypeToJsonField(node, L"content", value.content); + ConvertCustomTypeToJsonField(node, L"children", value.children); return node; } - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary>(const ::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary & value) + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder>(const ::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder & value) { auto node = Ptr(new glr::json::JsonObject); + ConvertCustomTypeToJsonField(node, L"diffsInOrder", value.diffsInOrder); return node; } - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingCommand_Element>(const ::vl::presentation::remoteprotocol::RenderingCommand_Element & value) - { - auto node = Ptr(new glr::json::JsonObject); - ConvertCustomTypeToJsonField(node, L"rendering", value.rendering); - ConvertCustomTypeToJsonField(node, L"element", value.element); - return node; - } - - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingFrame>(const ::vl::presentation::remoteprotocol::RenderingFrame & value) + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::UnitTest_RenderingFrame>(const ::vl::presentation::remoteprotocol::UnitTest_RenderingFrame & value) { auto node = Ptr(new glr::json::JsonObject); ConvertCustomTypeToJsonField(node, L"frameId", value.frameId); ConvertCustomTypeToJsonField(node, L"frameName", value.frameName); ConvertCustomTypeToJsonField(node, L"windowSize", value.windowSize); ConvertCustomTypeToJsonField(node, L"elements", value.elements); - ConvertCustomTypeToJsonField(node, L"commands", value.commands); ConvertCustomTypeToJsonField(node, L"root", value.root); return node; } - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingTrace>(const ::vl::presentation::remoteprotocol::RenderingTrace & value) + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::UnitTest_RenderingTrace>(const ::vl::presentation::remoteprotocol::UnitTest_RenderingTrace & value) { auto node = Ptr(new glr::json::JsonObject); ConvertCustomTypeToJsonField(node, L"createdElements", value.createdElements); @@ -41683,6 +43614,18 @@ namespace vl::presentation::remoteprotocol #undef ERROR_MESSAGE_PREFIX } + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_DiffType>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom_DiffType& value) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_DiffType>(Ptr, ::vl::presentation::remoteprotocol::RenderingDom_DiffType&)#" + auto jsonNode = node.Cast(); + CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); + if (jsonNode->content.value == L"Deleted") value = ::vl::presentation::remoteprotocol::RenderingDom_DiffType::Deleted; else + if (jsonNode->content.value == L"Created") value = ::vl::presentation::remoteprotocol::RenderingDom_DiffType::Created; else + if (jsonNode->content.value == L"Modified") value = ::vl::presentation::remoteprotocol::RenderingDom_DiffType::Modified; else + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported enum value."); +#undef ERROR_MESSAGE_PREFIX + } + template<> void ConvertJsonToCustomType<::vl::presentation::NativeCoordinate>(vl::Ptr node, ::vl::presentation::NativeCoordinate& value) { #define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::NativeCoordinate>(Ptr, ::vl::presentation::NativeCoordinate&)#" @@ -42227,6 +44170,7 @@ namespace vl::presentation::remoteprotocol CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); for (auto field : jsonNode->fields) { + if (field->name.value == L"id") ConvertJsonToCustomType(field->value, value.id); else if (field->name.value == L"hitTestResult") ConvertJsonToCustomType(field->value, value.hitTestResult); else if (field->name.value == L"cursor") ConvertJsonToCustomType(field->value, value.cursor); else if (field->name.value == L"bounds") ConvertJsonToCustomType(field->value, value.bounds); else @@ -42280,9 +44224,9 @@ namespace vl::presentation::remoteprotocol #undef ERROR_MESSAGE_PREFIX } - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom& value) + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDomContent>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDomContent& value) { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom>(Ptr, ::vl::presentation::remoteprotocol::RenderingDom&)#" +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDomContent>(Ptr, ::vl::presentation::remoteprotocol::RenderingDomContent&)#" auto jsonNode = node.Cast(); CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); for (auto field : jsonNode->fields) @@ -42292,54 +44236,58 @@ namespace vl::presentation::remoteprotocol if (field->name.value == L"element") ConvertJsonToCustomType(field->value, value.element); else if (field->name.value == L"bounds") ConvertJsonToCustomType(field->value, value.bounds); else if (field->name.value == L"validArea") ConvertJsonToCustomType(field->value, value.validArea); else + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported struct member."); + } +#undef ERROR_MESSAGE_PREFIX + } + + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom& value) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom>(Ptr, ::vl::presentation::remoteprotocol::RenderingDom&)#" + auto jsonNode = node.Cast(); + CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); + for (auto field : jsonNode->fields) + { + if (field->name.value == L"id") ConvertJsonToCustomType(field->value, value.id); else + if (field->name.value == L"content") ConvertJsonToCustomType(field->value, value.content); else if (field->name.value == L"children") ConvertJsonToCustomType(field->value, value.children); else CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported struct member."); } #undef ERROR_MESSAGE_PREFIX } - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary& value) + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_Diff>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom_Diff& value) { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary>(Ptr, ::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary&)#" +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_Diff>(Ptr, ::vl::presentation::remoteprotocol::RenderingDom_Diff&)#" auto jsonNode = node.Cast(); CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); for (auto field : jsonNode->fields) { - if (field->name.value == L"boundary") ConvertJsonToCustomType(field->value, value.boundary); else + if (field->name.value == L"id") ConvertJsonToCustomType(field->value, value.id); else + if (field->name.value == L"diffType") ConvertJsonToCustomType(field->value, value.diffType); else + if (field->name.value == L"content") ConvertJsonToCustomType(field->value, value.content); else + if (field->name.value == L"children") ConvertJsonToCustomType(field->value, value.children); else CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported struct member."); } #undef ERROR_MESSAGE_PREFIX } - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary& value) + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder& value) { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary>(Ptr, ::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary&)#" +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder>(Ptr, ::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder&)#" auto jsonNode = node.Cast(); CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); for (auto field : jsonNode->fields) { + if (field->name.value == L"diffsInOrder") ConvertJsonToCustomType(field->value, value.diffsInOrder); else CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported struct member."); } #undef ERROR_MESSAGE_PREFIX } - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_Element>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingCommand_Element& value) + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::UnitTest_RenderingFrame>(vl::Ptr node, ::vl::presentation::remoteprotocol::UnitTest_RenderingFrame& value) { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_Element>(Ptr, ::vl::presentation::remoteprotocol::RenderingCommand_Element&)#" - auto jsonNode = node.Cast(); - CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); - for (auto field : jsonNode->fields) - { - if (field->name.value == L"rendering") ConvertJsonToCustomType(field->value, value.rendering); else - if (field->name.value == L"element") ConvertJsonToCustomType(field->value, value.element); else - CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported struct member."); - } -#undef ERROR_MESSAGE_PREFIX - } - - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingFrame>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingFrame& value) - { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingFrame>(Ptr, ::vl::presentation::remoteprotocol::RenderingFrame&)#" +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::UnitTest_RenderingFrame>(Ptr, ::vl::presentation::remoteprotocol::UnitTest_RenderingFrame&)#" auto jsonNode = node.Cast(); CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); for (auto field : jsonNode->fields) @@ -42348,16 +44296,15 @@ namespace vl::presentation::remoteprotocol if (field->name.value == L"frameName") ConvertJsonToCustomType(field->value, value.frameName); else if (field->name.value == L"windowSize") ConvertJsonToCustomType(field->value, value.windowSize); else if (field->name.value == L"elements") ConvertJsonToCustomType(field->value, value.elements); else - if (field->name.value == L"commands") ConvertJsonToCustomType(field->value, value.commands); else if (field->name.value == L"root") ConvertJsonToCustomType(field->value, value.root); else CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unsupported struct member."); } #undef ERROR_MESSAGE_PREFIX } - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingTrace>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingTrace& value) + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::UnitTest_RenderingTrace>(vl::Ptr node, ::vl::presentation::remoteprotocol::UnitTest_RenderingTrace& value) { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingTrace>(Ptr, ::vl::presentation::remoteprotocol::RenderingTrace&)#" +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::ConvertJsonToCustomType<::vl::presentation::remoteprotocol::UnitTest_RenderingTrace>(Ptr, ::vl::presentation::remoteprotocol::UnitTest_RenderingTrace&)#" auto jsonNode = node.Cast(); CHECK_ERROR(jsonNode, ERROR_MESSAGE_PREFIX L"Json node does not match the expected type."); for (auto field : jsonNode->fields) @@ -42374,6 +44321,1139 @@ namespace vl::presentation::remoteprotocol } +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTERENDERER\GUIREMOTERENDERERSINGLE.CPP +***********************************************************************/ + +namespace vl::presentation::remote_renderer +{ + using namespace elements; + using namespace remoteprotocol; + + remoteprotocol::ScreenConfig GuiRemoteRendererSingle::GetScreenConfig(INativeScreen* screen) + { + ScreenConfig response; + response.bounds = screen->GetBounds(); + response.clientBounds = screen->GetClientBounds(); + response.scalingX = screen->GetScalingX(); + response.scalingY = screen->GetScalingY(); + return response; + } + + remoteprotocol::WindowSizingConfig GuiRemoteRendererSingle::GetWindowSizingConfig() + { + WindowSizingConfig response; + response.bounds = window->GetBounds(); + response.clientBounds = window->GetClientBoundsInScreen(); + response.sizeState = window->GetSizeState(); + response.customFramePadding = window->GetCustomFramePadding(); + return response; + } + + void GuiRemoteRendererSingle::UpdateConfigsIfNecessary() + { + if (screen) + { + auto currentScreen = GetCurrentController()->ScreenService()->GetScreen(window); + if (screen != currentScreen) + { + screen = currentScreen; + events->OnControllerScreenUpdated(GetScreenConfig(screen)); + } + + auto newWindowSizingConfig = GetWindowSizingConfig(); + if ( + newWindowSizingConfig.bounds != windowSizingConfig.bounds || + newWindowSizingConfig.clientBounds != windowSizingConfig.clientBounds) + { + windowSizingConfig = newWindowSizingConfig; + if (!updatingBounds) + { + events->OnWindowBoundsUpdated(windowSizingConfig); + } + } + else if ( + newWindowSizingConfig.sizeState != windowSizingConfig.sizeState || + newWindowSizingConfig.customFramePadding != windowSizingConfig.customFramePadding) + { + windowSizingConfig = newWindowSizingConfig; + events->OnWindowBoundsUpdated(windowSizingConfig); + } + } + } + + void GuiRemoteRendererSingle::NativeWindowDestroying(INativeWindow* _window) + { + if (window == _window) + { + window->UninstallListener(this); + window = nullptr; + } + } + + void GuiRemoteRendererSingle::Opened() + { + events->OnControllerConnect(); + } + + void GuiRemoteRendererSingle::BeforeClosing(bool& cancel) + { + if (!disconnectingFromCore) + { + cancel = true; + events->OnControllerRequestExit(); + } + } + + void GuiRemoteRendererSingle::AfterClosing() + { + renderingDom = nullptr; + availableElements.Clear(); + availableImages.Clear(); + } + + void GuiRemoteRendererSingle::Closed() + { + } + + void GuiRemoteRendererSingle::Moved() + { + UpdateConfigsIfNecessary(); + } + + void GuiRemoteRendererSingle::DpiChanged(bool preparing) + { + if (preparing) + { + UpdateRenderTarget(nullptr); + } + else + { + GetGuiGraphicsResourceManager()->RecreateRenderTarget(window); + UpdateRenderTarget(GetGuiGraphicsResourceManager()->GetRenderTarget(window)); + UpdateConfigsIfNecessary(); + } + } + + void GuiRemoteRendererSingle::RenderingAsActivated() + { + if (disconnectingFromCore) return; + events->OnWindowActivatedUpdated(true); + } + + void GuiRemoteRendererSingle::RenderingAsDeactivated() + { + if (disconnectingFromCore) return; + events->OnWindowActivatedUpdated(false); + } + + GuiRemoteRendererSingle::GuiRemoteRendererSingle() + { + } + + GuiRemoteRendererSingle::~GuiRemoteRendererSingle() + { + } + + void GuiRemoteRendererSingle::RegisterMainWindow(INativeWindow* _window) + { + window = _window; + window->InstallListener(this); + GetCurrentController()->CallbackService()->InstallListener(this); + } + + void GuiRemoteRendererSingle::UnregisterMainWindow() + { + GetCurrentController()->CallbackService()->UninstallListener(this); + } + + void GuiRemoteRendererSingle::ForceExitByFatelError() + { + if (window) + { + disconnectingFromCore = true; + window->Hide(true); + } + } + + WString GuiRemoteRendererSingle::GetExecutablePath() + { + CHECK_FAIL(L"This function should not be called!"); + } + + void GuiRemoteRendererSingle::Initialize(IGuiRemoteProtocolEvents* _events) + { + events = _events; + } + + void GuiRemoteRendererSingle::Submit(bool& disconnected) + { + CHECK_FAIL(L"This function should not be called!"); + } + + void GuiRemoteRendererSingle::ProcessRemoteEvents() + { + CHECK_FAIL(L"This function should not be called!"); + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTERENDERER\GUIREMOTERENDERERSINGLE_CONTROLLER.CPP +***********************************************************************/ + +namespace vl::presentation::remote_renderer +{ + using namespace collections; + using namespace remoteprotocol; + + void GuiRemoteRendererSingle::RequestControllerGetFontConfig(vint id) + { + FontConfig response; + auto rs = GetCurrentController()->ResourceService(); + response.defaultFont = rs->GetDefaultFont(); + response.supportedFonts = Ptr(new List); + rs->EnumerateFonts(*response.supportedFonts.Obj()); + events->RespondControllerGetFontConfig(id, response); + } + + void GuiRemoteRendererSingle::RequestControllerGetScreenConfig(vint id) + { + auto primary = screen ? screen : GetCurrentController()->ScreenService()->GetScreen((vint)0); + events->RespondControllerGetScreenConfig(id, GetScreenConfig(primary)); + } + + void GuiRemoteRendererSingle::RequestControllerConnectionEstablished() + { + } + + void GuiRemoteRendererSingle::RequestControllerConnectionStopped() + { + if (window) + { + disconnectingFromCore = true; + window->ReleaseCapture(); + window->Hide(true); + } + } +} + + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTERENDERER\GUIREMOTERENDERERSINGLE_IO.CPP +***********************************************************************/ + +namespace vl::presentation::remote_renderer +{ + using namespace remoteprotocol; + +/*********************************************************************** +* Rendering (Commands) +***********************************************************************/ + + void GuiRemoteRendererSingle::RequestIOUpdateGlobalShortcutKey(const Ptr>& arguments) + { + CHECK_ERROR(arguments->Count() == 0, L"Not Implemented"); + } + + void GuiRemoteRendererSingle::RequestIORequireCapture() + { + window->RequireCapture(); + } + + void GuiRemoteRendererSingle::RequestIOReleaseCapture() + { + window->ReleaseCapture(); + } + + void GuiRemoteRendererSingle::RequestIOIsKeyPressing(vint id, const VKEY& arguments) + { + CHECK_FAIL(L"Not Implemented"); + } + + void GuiRemoteRendererSingle::RequestIOIsKeyToggled(vint id, const VKEY& arguments) + { + CHECK_FAIL(L"Not Implemented"); + } + +/*********************************************************************** +* Rendering (INativeWindow) +***********************************************************************/ + + void GuiRemoteRendererSingle::LeftButtonDown(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Left; + arguments.info = info; + events->OnIOButtonDown(arguments); + } + + void GuiRemoteRendererSingle::LeftButtonUp(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Left; + arguments.info = info; + events->OnIOButtonUp(arguments); + } + + void GuiRemoteRendererSingle::LeftButtonDoubleClick(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Left; + arguments.info = info; + events->OnIOButtonDoubleClick(arguments); + } + + void GuiRemoteRendererSingle::RightButtonDown(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Right; + arguments.info = info; + events->OnIOButtonDown(arguments); + } + + void GuiRemoteRendererSingle::RightButtonUp(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Right; + arguments.info = info; + events->OnIOButtonUp(arguments); + } + + void GuiRemoteRendererSingle::RightButtonDoubleClick(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Right; + arguments.info = info; + events->OnIOButtonDoubleClick(arguments); + } + + void GuiRemoteRendererSingle::MiddleButtonDown(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Middle; + arguments.info = info; + events->OnIOButtonDown(arguments); + } + + void GuiRemoteRendererSingle::MiddleButtonUp(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Middle; + arguments.info = info; + events->OnIOButtonUp(arguments); + } + + void GuiRemoteRendererSingle::MiddleButtonDoubleClick(const NativeWindowMouseInfo& info) + { + IOMouseInfoWithButton arguments; + arguments.button = IOMouseButton::Middle; + arguments.info = info; + events->OnIOButtonDoubleClick(arguments); + } + + void GuiRemoteRendererSingle::HorizontalWheel(const NativeWindowMouseInfo& info) + { + } + + void GuiRemoteRendererSingle::VerticalWheel(const NativeWindowMouseInfo& info) + { + } + + void GuiRemoteRendererSingle::MouseMoving(const NativeWindowMouseInfo& info) + { + if (renderingDom) + { + INativeWindowListener::HitTestResult hitTestResult = INativeWindowListener::NoDecision; + INativeCursor* cursor = nullptr; + HitTest(renderingDom, window->Convert(NativePoint{ info.x,info.y }), hitTestResult, cursor); + window->SetWindowCursor(cursor); + } + events->OnIOMouseMoving(info); + } + + void GuiRemoteRendererSingle::MouseEntered() + { + events->OnIOMouseEntered(); + } + + void GuiRemoteRendererSingle::MouseLeaved() + { + events->OnIOMouseLeaved(); + } + + void GuiRemoteRendererSingle::KeyDown(const NativeWindowKeyInfo& info) + { + events->OnIOKeyDown(info); + } + + void GuiRemoteRendererSingle::KeyUp(const NativeWindowKeyInfo& info) + { + if (!info.ctrl && !info.shift && info.code == VKEY::KEY_MENU) + { + window->SupressAlt(); + } + events->OnIOKeyUp(info); + } + + void GuiRemoteRendererSingle::Char(const NativeWindowCharInfo& info) + { + events->OnIOChar(info); + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTERENDERER\GUIREMOTERENDERERSINGLE_MAINWINDOW.CPP +***********************************************************************/ + +namespace vl::presentation::remote_renderer +{ + using namespace remoteprotocol; + + void GuiRemoteRendererSingle::RequestWindowGetBounds(vint id) + { + events->RespondWindowGetBounds(id, GetWindowSizingConfig()); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetBounds(const NativeRect& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestWindowNotifySetBounds(const NativeRect&)#" + CHECK_ERROR(!updatingBounds, ERROR_MESSAGE_PREFIX L"This function cannot be called recursively."); + + updatingBounds = true; + if (!screen) + { + auto primary = GetCurrentController()->ScreenService()->GetScreen((vint)0); + NativeRect screenBounds = primary->GetBounds(); + NativeRect windowBounds = arguments; + windowBounds.x1 = (screenBounds.Width() - windowBounds.Width()) / 2; + windowBounds.y1 = (screenBounds.Height() - windowBounds.Height()) / 2; + window->SetBounds(windowBounds); + + screen = primary; + windowSizingConfig = GetWindowSizingConfig(); + } + else + { + window->SetBounds(arguments); + } + updatingBounds = false; +#undef ERROR_MESSAGE_PREFIX + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetTitle(const WString& arguments) + { + window->SetTitle(arguments); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetEnabled(const bool& arguments) + { + if (arguments) + { + window->Enable(); + } + else + { + window->Disable(); + } + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetTopMost(const bool& arguments) + { + window->SetTopMost(arguments); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetShowInTaskBar(const bool& arguments) + { + if (arguments) + { + window->ShowInTaskBar(); + } + else + { + window->HideInTaskBar(); + } + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetClientSize(const NativeSize& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestWindowNotifySetClientSize(const NativeSize&)#" + CHECK_ERROR(screen, ERROR_MESSAGE_PREFIX L"This function cannot be called before RequestWindowNotifySetBounds."); + + window->SetClientSize(arguments); +#undef ERROR_MESSAGE_PREFIX + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetCustomFrameMode(const bool& arguments) + { + if (window->IsCustomFrameModeEnabled() != arguments) + { + if (arguments) + { + window->EnableCustomFrameMode(); + } + else + { + window->DisableCustomFrameMode(); + } + UpdateConfigsIfNecessary(); + } + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetMaximizedBox(const bool& arguments) + { + window->SetMaximizedBox(arguments); + UpdateConfigsIfNecessary(); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetMinimizedBox(const bool& arguments) + { + window->SetMinimizedBox(arguments); + UpdateConfigsIfNecessary(); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetBorder(const bool& arguments) + { + window->SetBorder(arguments); + UpdateConfigsIfNecessary(); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetSizeBox(const bool& arguments) + { + window->SetSizeBox(arguments); + UpdateConfigsIfNecessary(); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetIconVisible(const bool& arguments) + { + window->SetIconVisible(arguments); + UpdateConfigsIfNecessary(); + } + + void GuiRemoteRendererSingle::RequestWindowNotifySetTitleBar(const bool& arguments) + { + window->SetTitleBar(arguments); + UpdateConfigsIfNecessary(); + } + + void GuiRemoteRendererSingle::RequestWindowNotifyActivate() + { + window->SetActivate(); + } + + void GuiRemoteRendererSingle::RequestWindowNotifyShow(const remoteprotocol::WindowShowing& arguments) + { + if (arguments.sizeState != window->GetSizeState()) + { + if (arguments.activate) + { + window->SetActivate(); + } + switch (arguments.sizeState) + { + case INativeWindow::Minimized: + window->ShowMinimized(); + break; + case INativeWindow::Restored: + window->ShowRestored(); + break; + case INativeWindow::Maximized: + window->ShowMaximized(); + break; + } + } + } +} + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTERENDERER\GUIREMOTERENDERERSINGLE_RENDERING.CPP +***********************************************************************/ + +namespace vl::presentation::remote_renderer +{ + using namespace collections; + using namespace elements; + using namespace remoteprotocol; + + Alignment GuiRemoteRendererSingle::GetAlignment(remoteprotocol::ElementHorizontalAlignment alignment) + { + switch (alignment) + { + case remoteprotocol::ElementHorizontalAlignment::Left: return Alignment::Left; + case remoteprotocol::ElementHorizontalAlignment::Right: return Alignment::Right; + default: return Alignment::Center; + } + } + + Alignment GuiRemoteRendererSingle::GetAlignment(remoteprotocol::ElementVerticalAlignment alignment) + { + switch (alignment) + { + case remoteprotocol::ElementVerticalAlignment::Top: return Alignment::Top; + case remoteprotocol::ElementVerticalAlignment::Bottom: return Alignment::Bottom; + default: return Alignment::Center; + } + } + +/*********************************************************************** +* Rendering +***********************************************************************/ + + void GuiRemoteRendererSingle::RequestRendererCreated(const Ptr>& arguments) + { + if (arguments) + { + for (auto&& rc : *arguments.Obj()) + { + Ptr element; + switch (rc.type) + { + case RendererType::FocusRectangle: + element = Ptr(GuiFocusRectangleElement::Create()); + break; + case RendererType::SolidBorder: + element = Ptr(GuiSolidBorderElement::Create()); + break; + case RendererType::SinkBorder: + element = Ptr(Gui3DBorderElement::Create()); + break; + case RendererType::SinkSplitter: + element = Ptr(Gui3DSplitterElement::Create()); + break; + case RendererType::SolidBackground: + element = Ptr(GuiSolidBackgroundElement::Create()); + break; + case RendererType::GradientBackground: + element = Ptr(GuiGradientBackgroundElement::Create()); + break; + case RendererType::InnerShadow: + element = Ptr(GuiInnerShadowElement::Create()); + break; + case RendererType::SolidLabel: + element = Ptr(GuiSolidLabelElement::Create()); + break; + case RendererType::Polygon: + element = Ptr(GuiPolygonElement::Create()); + break; + case RendererType::ImageFrame: + element = Ptr(GuiImageFrameElement::Create()); + break; + default:; + } + + element->GetRenderer()->SetRenderTarget(GetGuiGraphicsResourceManager()->GetRenderTarget(window)); + + if (availableElements.Keys().Contains(rc.id)) + { + availableElements.Set(rc.id, element); + } + else + { + availableElements.Add(rc.id, element); + } + } + } + } + + void GuiRemoteRendererSingle::RequestRendererDestroyed(const Ptr>& arguments) + { + if (arguments) + { + for (auto id : *arguments.Obj()) + { + availableElements.Remove(id); + solidLabelMeasurings.Remove(id); + } + } + } + + void GuiRemoteRendererSingle::RequestRendererBeginRendering(const remoteprotocol::ElementBeginRendering& arguments) + { + } + + void GuiRemoteRendererSingle::RequestRendererEndRendering(vint id) + { + events->RespondRendererEndRendering(id, elementMeasurings); + elementMeasurings = {}; + fontHeightMeasurings.Clear(); + } + +/*********************************************************************** +* Rendering (Elemnents) +***********************************************************************/ + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_SolidBorder(const remoteprotocol::ElementDesc_SolidBorder& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetColor(arguments.borderColor); + element->SetShape(arguments.shape); + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_SinkBorder(const remoteprotocol::ElementDesc_SinkBorder& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetColors(arguments.leftTopColor, arguments.rightBottomColor); + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_SinkSplitter(const remoteprotocol::ElementDesc_SinkSplitter& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetColors(arguments.leftTopColor, arguments.rightBottomColor); + element->SetDirection(arguments.direction); + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_SolidBackground(const remoteprotocol::ElementDesc_SolidBackground& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetColor(arguments.backgroundColor); + element->SetShape(arguments.shape); + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_GradientBackground(const remoteprotocol::ElementDesc_GradientBackground& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetColors(arguments.leftTopColor, arguments.rightBottomColor); + element->SetDirection(arguments.direction); + element->SetShape(arguments.shape); + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_InnerShadow(const remoteprotocol::ElementDesc_InnerShadow& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetColor(arguments.shadowColor); + element->SetThickness(arguments.thickness); + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_Polygon(const remoteprotocol::ElementDesc_Polygon& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetSize(arguments.size); + element->SetBorderColor(arguments.borderColor); + element->SetBackgroundColor(arguments.backgroundColor); + + if (arguments.points && arguments.points->Count() > 0) + { + element->SetPoints(&arguments.points->Get(0), arguments.points->Count()); + } + } + +/*********************************************************************** +* Rendering (Elemnents -- Label) +***********************************************************************/ + + void GuiRemoteRendererSingle::StoreLabelMeasuring(vint id, remoteprotocol::ElementSolidLabelMeasuringRequest request, Ptr solidLabel, Size minSize) + { + switch (request) + { + case ElementSolidLabelMeasuringRequest::FontHeight: + { + Pair key = { solidLabel->GetFont().fontFamily,solidLabel->GetFont().size }; + if (fontHeightMeasurings.Contains(key)) return; + fontHeightMeasurings.Add(key); + + ElementMeasuring_FontHeight response; + response.fontFamily = key.key; + response.fontSize = key.value; + response.height = minSize.y; + + if (!elementMeasurings.fontHeights) + { + elementMeasurings.fontHeights = Ptr(new List); + } + elementMeasurings.fontHeights->Add(response); + } + break; + case ElementSolidLabelMeasuringRequest::TotalSize: + { + ElementMeasuring_ElementMinSize response; + response.id = id; + response.minSize = minSize; + + if (!elementMeasurings.minSizes) + { + elementMeasurings.minSizes = Ptr(new List); + } + elementMeasurings.minSizes->Add(response); + } + break; + } + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_SolidLabel(const remoteprotocol::ElementDesc_SolidLabel& arguments) + { + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetColor(arguments.textColor); + element->SetAlignments(GetAlignment(arguments.horizontalAlignment), GetAlignment(arguments.verticalAlignment)); + element->SetWrapLine(arguments.wrapLine); + element->SetWrapLineHeightCalculation(arguments.wrapLineHeightCalculation); + element->SetEllipse(arguments.ellipse); + element->SetMultiline(arguments.multiline); + + if (arguments.font) + { + element->SetFont(arguments.font.Value()); + } + if (arguments.text) + { + element->SetText(arguments.text.Value()); + } + + if (arguments.measuringRequest) + { + SolidLabelMeasuring measuring; + measuring.request = arguments.measuringRequest.Value(); + index = solidLabelMeasurings.Keys().IndexOf(arguments.id); + if (solidLabelMeasurings.Keys().Contains(arguments.id)) + { + solidLabelMeasurings.Set(arguments.id, measuring); + } + else + { + solidLabelMeasurings.Add(arguments.id, measuring); + } + + StoreLabelMeasuring(arguments.id, measuring.request, element, element->GetRenderer()->GetMinSize()); + } + } + +/*********************************************************************** +* Rendering (Elements -- Image) +***********************************************************************/ + + remoteprotocol::ImageMetadata GuiRemoteRendererSingle::CreateImageMetadata(vint id, INativeImage* image) + { + ImageMetadata response; + response.id = id; + response.format = image->GetFormat(); + response.frames = Ptr(new List); + for (vint i = 0; i < image->GetFrameCount(); i++) + { + auto frame = image->GetFrame(i); + response.frames->Add({ frame->GetSize() }); + } + + return response; + } + + remoteprotocol::ImageMetadata GuiRemoteRendererSingle::CreateImage(const remoteprotocol::ImageCreation& arguments) + { + arguments.imageData->SeekFromBegin(0); + auto image = GetCurrentController()->ImageService()->CreateImageFromStream(*arguments.imageData.Obj()); + if (availableImages.Keys().Contains(arguments.id)) + { + availableImages.Set(arguments.id, image); + } + else + { + availableImages.Add(arguments.id, image); + } + return CreateImageMetadata(arguments.id, image.Obj()); + } + + void GuiRemoteRendererSingle::RequestImageCreated(vint id, const remoteprotocol::ImageCreation& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestImageCreated(const ImageCreation&)#" + CHECK_ERROR(!arguments.imageDataOmitted && arguments.imageData, ERROR_MESSAGE_PREFIX L"Binary content of the image is missing."); + + events->RespondImageCreated(id, CreateImage(arguments)); +#undef ERROR_MESSAGE_PREFIX + } + + void GuiRemoteRendererSingle::RequestImageDestroyed(const vint& arguments) + { + availableImages.Remove(arguments); + } + + void GuiRemoteRendererSingle::RequestRendererUpdateElement_ImageFrame(const remoteprotocol::ElementDesc_ImageFrame& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestRendererUpdateElement_ImageFrame(const arguments&)#" + + vint index = availableElements.Keys().IndexOf(arguments.id); + if (index == -1) return; + auto element = availableElements.Values()[index].Cast(); + if (!element) return; + + element->SetAlignments(GetAlignment(arguments.horizontalAlignment), GetAlignment(arguments.verticalAlignment)); + element->SetStretch(arguments.stretch); + element->SetEnabled(arguments.enabled); + + if (arguments.imageId && arguments.imageCreation) + { + CHECK_ERROR(arguments.imageId.Value() == arguments.imageCreation.Value().id, ERROR_MESSAGE_PREFIX L"imageId and imageCreation.id must be identical."); + } + + if (arguments.imageId) + { + if (arguments.imageCreation && !elementMeasurings.createdImages) + { + elementMeasurings.createdImages = Ptr(new List); + } + + vint index = availableImages.Keys().IndexOf(arguments.imageId.Value()); + if (index == -1) + { + CHECK_ERROR(arguments.imageCreation && !arguments.imageCreation.Value().imageDataOmitted && arguments.imageCreation.Value().imageData, ERROR_MESSAGE_PREFIX L"Binary content of the image is missing."); + + auto response = CreateImage(arguments.imageCreation.Value()); + element->SetImage(availableImages[response.id], arguments.imageFrame); + elementMeasurings.createdImages->Add(response); + } + else + { + auto image = availableImages.Values()[index]; + element->SetImage(image, arguments.imageFrame); + if (arguments.imageCreation) + { + elementMeasurings.createdImages->Add(CreateImageMetadata(arguments.imageId.Value(), image.Obj())); + } + } + } +#undef ERROR_MESSAGE_PREFIX + } + +/*********************************************************************** +* Rendering (Dom) +***********************************************************************/ + + void GuiRemoteRendererSingle::CheckDom() + { + needRefresh = true; + } + + void GuiRemoteRendererSingle::RequestRendererRenderDom(const Ptr& arguments) + { + renderingDom = arguments; + if (renderingDom) + { + BuildDomIndex(renderingDom, renderingDomIndex); + } + CheckDom(); + } + + void GuiRemoteRendererSingle::RequestRendererRenderDomDiff(const remoteprotocol::RenderingDom_DiffsInOrder& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestRendererRenderDomDiff(const RenderingDom_DiffsInOrder&)#" + CHECK_ERROR(renderingDom, ERROR_MESSAGE_PREFIX L"This function must be called after RequestRendererRenderDom."); + + UpdateDomInplace(renderingDom, renderingDomIndex, arguments); + CheckDom(); +#undef ERROR_MESSAGE_PREFIX + } + +/*********************************************************************** +* Rendering (Commands) +***********************************************************************/ + + void GuiRemoteRendererSingle::RequestRendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestRendererBeginBoundary(const ElementBoundary&)#" + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"The current implementation require dom-diff enabled in core side."); +#undef ERROR_MESSAGE_PREFIX + } + + void GuiRemoteRendererSingle::RequestRendererEndBoundary() + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestRendererEndBoundary()#" + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"The current implementation require dom-diff enabled in core side."); +#undef ERROR_MESSAGE_PREFIX + } + + void GuiRemoteRendererSingle::RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remote_renderer::GuiRemoteRendererSingle::RequestRendererRenderElement(const ElementRendering&)#" + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"The current implementation require dom-diff enabled in core side."); +#undef ERROR_MESSAGE_PREFIX + } + +/*********************************************************************** +* Rendering (INativeWindow) +***********************************************************************/ + + void GuiRemoteRendererSingle::UpdateRenderTarget(elements::IGuiGraphicsRenderTarget* rt) + { + for (auto element : availableElements.Values()) + { + element->GetRenderer()->SetRenderTarget(rt); + } + } + + void GuiRemoteRendererSingle::Render(Ptr dom, elements::IGuiGraphicsRenderTarget* rt) + { + if (dom->content.element) + { + vint index = availableElements.Keys().IndexOf(dom->content.element.Value()); + if (index != -1) + { + auto element = availableElements.Values()[index]; + rt->PushClipper(dom->content.validArea, nullptr); + element->GetRenderer()->Render(dom->content.bounds); + rt->PopClipper(nullptr); + + if (auto solidLabel = element.Cast()) + { + index = solidLabelMeasurings.Keys().IndexOf(dom->content.element.Value()); + if (index != -1) + { + auto& measuring = const_cast(solidLabelMeasurings.Values()[index]); + auto minSize = element->GetRenderer()->GetMinSize(); + + bool measuringChanged = false; + if (!measuring.minSize) + { + measuringChanged = true; + } + else switch (measuring.request) + { + case ElementSolidLabelMeasuringRequest::FontHeight: + if (measuring.minSize.Value().y != minSize.y) + { + measuringChanged = true; + } + break; + case ElementSolidLabelMeasuringRequest::TotalSize: + if (measuring.minSize.Value() != minSize) + { + measuringChanged = true; + } + break; + } + + measuring.minSize = minSize; + if (measuringChanged) + { + StoreLabelMeasuring(dom->content.element.Value(), measuring.request, solidLabel, minSize); + } + } + } + } + } + + if (dom->children) + { + for (auto child : *dom->children.Obj()) + { + if (child->content.validArea.Width() > 0 && child->content.validArea.Height()> 0) + { + Render(child, rt); + } + } + } + } + + void GuiRemoteRendererSingle::HitTestInternal(Ptr dom, Point location, Nullable& hitTestResult, Nullable& cursorType) + { + if (dom->children) + { + for (auto child : *dom->children.Obj()) + { + if (child->content.validArea.Contains(location)) + { + HitTestInternal(child, location, hitTestResult, cursorType); + + if (!hitTestResult && child->content.hitTestResult) + { + hitTestResult = child->content.hitTestResult; + } + if (!cursorType && child->content.cursor) + { + cursorType = child->content.cursor; + } + } + } + } + } + + void GuiRemoteRendererSingle::HitTest(Ptr dom, Point location, INativeWindowListener::HitTestResult& hitTestResult, INativeCursor*& cursor) + { + Nullable hitTestResultNullable; + Nullable cursorTypeNullable; + HitTestInternal(dom, location, hitTestResultNullable, cursorTypeNullable); + hitTestResult = hitTestResultNullable ? hitTestResultNullable.Value() : INativeWindowListener::NoDecision; + cursor = cursorTypeNullable ? GetCurrentController()->ResourceService()->GetSystemCursor(cursorTypeNullable.Value()) : GetCurrentController()->ResourceService()->GetDefaultSystemCursor(); + } + + void GuiRemoteRendererSingle::GlobalTimer() + { + if (!needRefresh) return; + needRefresh = false; + if (!window) return; + if (!renderingDom) return; + + supressPaint = true; + auto rt = GetGuiGraphicsResourceManager()->GetRenderTarget(window); + rt->StartRendering(); + Render(renderingDom, rt); + auto result = rt->StopRendering(); + window->RedrawContent(); + supressPaint = false; + + switch (result) + { + case RenderTargetFailure::ResizeWhileRendering: + GetGuiGraphicsResourceManager()->ResizeRenderTarget(window); + needRefresh = true; + break; + case RenderTargetFailure::LostDevice: + UpdateRenderTarget(nullptr); + GetGuiGraphicsResourceManager()->RecreateRenderTarget(window); + UpdateRenderTarget(GetGuiGraphicsResourceManager()->GetRenderTarget(window)); + needRefresh = true; + break; + default:; + } + } + + void GuiRemoteRendererSingle::Paint() + { + if (!supressPaint) + { + needRefresh = true; + } + } + + INativeWindowListener::HitTestResult GuiRemoteRendererSingle::HitTest(NativePoint location) + { + INativeWindowListener::HitTestResult hitTestResult = INativeWindowListener::NoDecision; + INativeCursor* cursor = nullptr; + if (renderingDom) + { + HitTest(renderingDom, window->Convert(location), hitTestResult, cursor); + } + return hitTestResult; + } +} + /*********************************************************************** .\RESOURCES\GUIDOCUMENT.CPP ***********************************************************************/ diff --git a/Import/GacUI.h b/Import/GacUI.h index e0484cbd..e76231be 100644 --- a/Import/GacUI.h +++ b/Import/GacUI.h @@ -4633,6 +4633,11 @@ namespace vl class GuiControlHost; } + namespace elements + { + class GuiRemoteGraphicsRenderTarget; + } + namespace compositions { class GuiGraphicsComposition_Trivial; @@ -4661,6 +4666,7 @@ Basic Construction friend class GuiWindowComposition; friend class controls::GuiControl; friend class GuiGraphicsHost; + friend class elements::GuiRemoteGraphicsRenderTarget; friend void InvokeOnCompositionStateChanged(compositions::GuiGraphicsComposition* composition); public: @@ -4695,6 +4701,7 @@ Basic Construction bool visible = true; bool transparentToMouse = false; MinSizeLimitation minSizeLimitation = MinSizeLimitation::NoLimit; + vint remoteId = -1; Ptr eventReceiver; GraphicsHostRecord* relatedHostRecord = nullptr; @@ -22145,12 +22152,12 @@ namespace vl::presentation::remoteprotocol struct ElementMeasuring_FontHeight; struct ElementMeasuring_ElementMinSize; struct ElementMeasurings; + struct RenderingDomContent; struct RenderingDom; - struct RenderingCommand_BeginBoundary; - struct RenderingCommand_EndBoundary; - struct RenderingCommand_Element; - struct RenderingFrame; - struct RenderingTrace; + struct RenderingDom_Diff; + struct RenderingDom_DiffsInOrder; + struct UnitTest_RenderingFrame; + struct UnitTest_RenderingTrace; } namespace vl::presentation::remoteprotocol { @@ -22192,12 +22199,12 @@ namespace vl::presentation::remoteprotocol template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::ElementMeasuring_FontHeight> { static constexpr const wchar_t* Name = L"ElementMeasuring_FontHeight"; }; template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::ElementMeasuring_ElementMinSize> { static constexpr const wchar_t* Name = L"ElementMeasuring_ElementMinSize"; }; template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::ElementMeasurings> { static constexpr const wchar_t* Name = L"ElementMeasurings"; }; + template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingDomContent> { static constexpr const wchar_t* Name = L"RenderingDomContent"; }; template<> struct JsonNameHelper<::vl::Ptr<::vl::presentation::remoteprotocol::RenderingDom>> { static constexpr const wchar_t* Name = L"RenderingDom"; }; - template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary> { static constexpr const wchar_t* Name = L"RenderingCommand_BeginBoundary"; }; - template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary> { static constexpr const wchar_t* Name = L"RenderingCommand_EndBoundary"; }; - template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingCommand_Element> { static constexpr const wchar_t* Name = L"RenderingCommand_Element"; }; - template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingFrame> { static constexpr const wchar_t* Name = L"RenderingFrame"; }; - template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingTrace> { static constexpr const wchar_t* Name = L"RenderingTrace"; }; + template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingDom_Diff> { static constexpr const wchar_t* Name = L"RenderingDom_Diff"; }; + template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder> { static constexpr const wchar_t* Name = L"RenderingDom_DiffsInOrder"; }; + template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::UnitTest_RenderingFrame> { static constexpr const wchar_t* Name = L"UnitTest_RenderingFrame"; }; + template<> struct JsonNameHelper<::vl::presentation::remoteprotocol::UnitTest_RenderingTrace> { static constexpr const wchar_t* Name = L"UnitTest_RenderingTrace"; }; } namespace vl::presentation::remoteprotocol { @@ -22244,7 +22251,14 @@ namespace vl::presentation::remoteprotocol UnsupportedDocument, }; - using ElementDescVariant = ::vl::Variant< + enum class RenderingDom_DiffType + { + Deleted, + Created, + Modified, + }; + + using UnitTest_ElementDescVariant = ::vl::Variant< ::vl::presentation::remoteprotocol::ElementDesc_SolidBorder, ::vl::presentation::remoteprotocol::ElementDesc_SinkBorder, ::vl::presentation::remoteprotocol::ElementDesc_SinkSplitter, @@ -22256,12 +22270,6 @@ namespace vl::presentation::remoteprotocol ::vl::presentation::remoteprotocol::ElementDesc_ImageFrame >; - using RenderingCommand = ::vl::Variant< - ::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary, - ::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary, - ::vl::presentation::remoteprotocol::RenderingCommand_Element - >; - struct FontConfig { ::vl::presentation::FontProperties defaultFont; @@ -22425,6 +22433,7 @@ namespace vl::presentation::remoteprotocol struct ElementBoundary { + ::vl::vint id; ::vl::Nullable<::vl::presentation::INativeWindowListener::HitTestResult> hitTestResult; ::vl::Nullable<::vl::presentation::INativeCursor::SystemCursorType> cursor; ::vl::presentation::Rect bounds; @@ -22451,47 +22460,50 @@ namespace vl::presentation::remoteprotocol ::vl::Ptr<::vl::collections::List<::vl::presentation::remoteprotocol::ImageMetadata>> createdImages; }; - struct RenderingDom + struct RenderingDomContent { ::vl::Nullable<::vl::presentation::INativeWindowListener::HitTestResult> hitTestResult; ::vl::Nullable<::vl::presentation::INativeCursor::SystemCursorType> cursor; ::vl::Nullable<::vl::vint> element; ::vl::presentation::Rect bounds; ::vl::presentation::Rect validArea; + }; + + struct RenderingDom + { + ::vl::vint id; + ::vl::presentation::remoteprotocol::RenderingDomContent content; ::vl::Ptr<::vl::collections::List<::vl::Ptr<::vl::presentation::remoteprotocol::RenderingDom>>> children; }; - struct RenderingCommand_BeginBoundary + struct RenderingDom_Diff { - ::vl::presentation::remoteprotocol::ElementBoundary boundary; + ::vl::vint id; + ::vl::presentation::remoteprotocol::RenderingDom_DiffType diffType; + ::vl::Nullable<::vl::presentation::remoteprotocol::RenderingDomContent> content; + ::vl::Ptr<::vl::collections::List<::vl::vint>> children; }; - struct RenderingCommand_EndBoundary + struct RenderingDom_DiffsInOrder { + ::vl::Ptr<::vl::collections::List<::vl::presentation::remoteprotocol::RenderingDom_Diff>> diffsInOrder; }; - struct RenderingCommand_Element - { - ::vl::presentation::remoteprotocol::ElementRendering rendering; - ::vl::Nullable<::vl::vint> element; - }; - - struct RenderingFrame + struct UnitTest_RenderingFrame { ::vl::vint frameId; ::vl::Nullable<::vl::WString> frameName; ::vl::presentation::remoteprotocol::WindowSizingConfig windowSize; - ::vl::Ptr<::vl::collections::Dictionary<::vl::vint, ::vl::presentation::remoteprotocol::ElementDescVariant>> elements; - ::vl::Ptr<::vl::collections::List<::vl::presentation::remoteprotocol::RenderingCommand>> commands; + ::vl::Ptr<::vl::collections::Dictionary<::vl::vint, ::vl::presentation::remoteprotocol::UnitTest_ElementDescVariant>> elements; ::vl::Ptr<::vl::presentation::remoteprotocol::RenderingDom> root; }; - struct RenderingTrace + struct UnitTest_RenderingTrace { ::vl::Ptr<::vl::collections::Dictionary<::vl::vint, ::vl::presentation::remoteprotocol::RendererType>> createdElements; ::vl::Ptr<::vl::presentation::remoteprotocol::ArrayMap<::vl::vint, ::vl::presentation::remoteprotocol::ImageCreation, &::vl::presentation::remoteprotocol::ImageCreation::id>> imageCreations; ::vl::Ptr<::vl::presentation::remoteprotocol::ArrayMap<::vl::vint, ::vl::presentation::remoteprotocol::ImageMetadata, &::vl::presentation::remoteprotocol::ImageMetadata::id>> imageMetadatas; - ::vl::Ptr<::vl::collections::List<::vl::presentation::remoteprotocol::RenderingFrame>> frames; + ::vl::Ptr<::vl::collections::List<::vl::presentation::remoteprotocol::UnitTest_RenderingFrame>> frames; }; template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::INativeWindowListener::HitTestResult>(const ::vl::presentation::INativeWindowListener::HitTestResult & value); @@ -22506,6 +22518,7 @@ namespace vl::presentation::remoteprotocol template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::ElementSolidLabelMeasuringRequest>(const ::vl::presentation::remoteprotocol::ElementSolidLabelMeasuringRequest & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::INativeImage::FormatType>(const ::vl::presentation::INativeImage::FormatType & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RendererType>(const ::vl::presentation::remoteprotocol::RendererType & value); + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom_DiffType>(const ::vl::presentation::remoteprotocol::RenderingDom_DiffType & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::NativeCoordinate>(const ::vl::presentation::NativeCoordinate & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::NativePoint>(const ::vl::presentation::NativePoint & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::NativeSize>(const ::vl::presentation::NativeSize & value); @@ -22544,12 +22557,12 @@ namespace vl::presentation::remoteprotocol template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::ElementMeasuring_FontHeight>(const ::vl::presentation::remoteprotocol::ElementMeasuring_FontHeight & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::ElementMeasuring_ElementMinSize>(const ::vl::presentation::remoteprotocol::ElementMeasuring_ElementMinSize & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::ElementMeasurings>(const ::vl::presentation::remoteprotocol::ElementMeasurings & value); + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDomContent>(const ::vl::presentation::remoteprotocol::RenderingDomContent & value); template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom>(const ::vl::presentation::remoteprotocol::RenderingDom & value); - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary>(const ::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary & value); - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary>(const ::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary & value); - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingCommand_Element>(const ::vl::presentation::remoteprotocol::RenderingCommand_Element & value); - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingFrame>(const ::vl::presentation::remoteprotocol::RenderingFrame & value); - template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingTrace>(const ::vl::presentation::remoteprotocol::RenderingTrace & value); + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom_Diff>(const ::vl::presentation::remoteprotocol::RenderingDom_Diff & value); + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder>(const ::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder & value); + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::UnitTest_RenderingFrame>(const ::vl::presentation::remoteprotocol::UnitTest_RenderingFrame & value); + template<> vl::Ptr ConvertCustomTypeToJson<::vl::presentation::remoteprotocol::UnitTest_RenderingTrace>(const ::vl::presentation::remoteprotocol::UnitTest_RenderingTrace & value); template<> void ConvertJsonToCustomType<::vl::presentation::INativeWindowListener::HitTestResult>(vl::Ptr node, ::vl::presentation::INativeWindowListener::HitTestResult& value); template<> void ConvertJsonToCustomType<::vl::presentation::INativeCursor::SystemCursorType>(vl::Ptr node, ::vl::presentation::INativeCursor::SystemCursorType& value); @@ -22563,6 +22576,7 @@ namespace vl::presentation::remoteprotocol template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::ElementSolidLabelMeasuringRequest>(vl::Ptr node, ::vl::presentation::remoteprotocol::ElementSolidLabelMeasuringRequest& value); template<> void ConvertJsonToCustomType<::vl::presentation::INativeImage::FormatType>(vl::Ptr node, ::vl::presentation::INativeImage::FormatType& value); template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RendererType>(vl::Ptr node, ::vl::presentation::remoteprotocol::RendererType& value); + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_DiffType>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom_DiffType& value); template<> void ConvertJsonToCustomType<::vl::presentation::NativeCoordinate>(vl::Ptr node, ::vl::presentation::NativeCoordinate& value); template<> void ConvertJsonToCustomType<::vl::presentation::NativePoint>(vl::Ptr node, ::vl::presentation::NativePoint& value); template<> void ConvertJsonToCustomType<::vl::presentation::NativeSize>(vl::Ptr node, ::vl::presentation::NativeSize& value); @@ -22601,12 +22615,12 @@ namespace vl::presentation::remoteprotocol template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::ElementMeasuring_FontHeight>(vl::Ptr node, ::vl::presentation::remoteprotocol::ElementMeasuring_FontHeight& value); template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::ElementMeasuring_ElementMinSize>(vl::Ptr node, ::vl::presentation::remoteprotocol::ElementMeasuring_ElementMinSize& value); template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::ElementMeasurings>(vl::Ptr node, ::vl::presentation::remoteprotocol::ElementMeasurings& value); + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDomContent>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDomContent& value); template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom& value); - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingCommand_BeginBoundary& value); - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingCommand_EndBoundary& value); - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingCommand_Element>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingCommand_Element& value); - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingFrame>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingFrame& value); - template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingTrace>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingTrace& value); + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_Diff>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom_Diff& value); + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder>(vl::Ptr node, ::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder& value); + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::UnitTest_RenderingFrame>(vl::Ptr node, ::vl::presentation::remoteprotocol::UnitTest_RenderingFrame& value); + template<> void ConvertJsonToCustomType<::vl::presentation::remoteprotocol::UnitTest_RenderingTrace>(vl::Ptr node, ::vl::presentation::remoteprotocol::UnitTest_RenderingTrace& value); #define GACUI_REMOTEPROTOCOL_MESSAGES(HANDLER)\ HANDLER(ControllerGetFontConfig, void, ::vl::presentation::remoteprotocol::FontConfig, NOREQ, RES, NODROP)\ @@ -22652,6 +22666,8 @@ namespace vl::presentation::remoteprotocol HANDLER(RendererRenderElement, ::vl::presentation::remoteprotocol::ElementRendering, void, REQ, NORES, NODROP)\ HANDLER(RendererEndBoundary, void, void, NOREQ, NORES, NODROP)\ HANDLER(RendererEndRendering, void, ::vl::presentation::remoteprotocol::ElementMeasurings, NOREQ, RES, NODROP)\ + HANDLER(RendererRenderDom, ::vl::Ptr<::vl::presentation::remoteprotocol::RenderingDom>, void, REQ, NORES, NODROP)\ + HANDLER(RendererRenderDomDiff, ::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder, void, REQ, NORES, NODROP)\ #define GACUI_REMOTEPROTOCOL_EVENTS(HANDLER)\ HANDLER(ControllerConnect, void, NOREQ, NODROP)\ @@ -22678,6 +22694,7 @@ namespace vl::presentation::remoteprotocol HANDLER(::vl::Ptr<::vl::collections::List<::vl::presentation::remoteprotocol::GlobalShortcutKey>>)\ HANDLER(::vl::Ptr<::vl::collections::List<::vl::presentation::remoteprotocol::RendererCreation>>)\ HANDLER(::vl::Ptr<::vl::collections::List<::vl::vint>>)\ + HANDLER(::vl::Ptr<::vl::presentation::remoteprotocol::RenderingDom>)\ HANDLER(::vl::WString)\ HANDLER(::vl::presentation::NativeRect)\ HANDLER(::vl::presentation::NativeSize)\ @@ -22695,6 +22712,7 @@ namespace vl::presentation::remoteprotocol HANDLER(::vl::presentation::remoteprotocol::ElementDesc_SolidLabel)\ HANDLER(::vl::presentation::remoteprotocol::ElementRendering)\ HANDLER(::vl::presentation::remoteprotocol::ImageCreation)\ + HANDLER(::vl::presentation::remoteprotocol::RenderingDom_DiffsInOrder)\ HANDLER(::vl::presentation::remoteprotocol::WindowShowing)\ HANDLER(::vl::vint)\ HANDLER(bool)\ @@ -22830,6 +22848,7 @@ GuiRemoteGraphicsImageService GuiRemoteGraphicsImageService(GuiRemoteController* _remote); ~GuiRemoteGraphicsImageService(); + void ResetImageMetadata(); void OnControllerConnect(); void OnControllerDisconnect(); void Initialize(); @@ -22891,6 +22910,7 @@ GuiRemoteGraphicsRenderTarget NativeSize canvasSize; vint usedFrameIds = 0; vint usedElementIds = 0; + vint usedCompositionIds = 0; RendererMap renderers; collections::SortedList createdRenderers; collections::SortedList destroyedRenderers; @@ -23008,6 +23028,7 @@ namespace vl::presentation::elements_remoteprotocol vint id = -1; vuint64_t renderingBatchId = 0; bool updated = true; + bool renderTargetChanged = false; void InitializeInternal(); void FinalizeInternal(); @@ -23246,7 +23267,7 @@ IGuiRemoteProtocol { public: virtual void Initialize(IGuiRemoteProtocolEvents* events) = 0; - virtual void Submit() = 0; + virtual void Submit(bool& disconnected) = 0; virtual void ProcessRemoteEvents() = 0; }; @@ -23256,9 +23277,12 @@ IGuiRemoteProtocol IGuiRemoteProtocolEvents* targetEvents = nullptr; }; + template + class GuiRemoteProtocolCombinator; + template requires(std::is_base_of_v) - class GuiRemoteProtocolCombinator : public Object, public virtual IGuiRemoteProtocol + class GuiRemoteProtocolCombinator : public Object, public virtual IGuiRemoteProtocol { protected: IGuiRemoteProtocol* targetProtocol = nullptr; @@ -23283,9 +23307,9 @@ IGuiRemoteProtocol targetProtocol->Initialize(&eventCombinator); } - void Submit() override + void Submit(bool& disconnected) override { - targetProtocol->Submit(); + targetProtocol->Submit(disconnected); } void ProcessRemoteEvents() override @@ -23293,6 +23317,900 @@ IGuiRemoteProtocol targetProtocol->ProcessRemoteEvents(); } }; + + template<> + class GuiRemoteProtocolCombinator : public Object, public virtual IGuiRemoteProtocol + { + protected: + IGuiRemoteProtocol* targetProtocol = nullptr; + IGuiRemoteProtocolEvents* events = nullptr; + + public: + GuiRemoteProtocolCombinator(IGuiRemoteProtocol* _protocol) + : targetProtocol(_protocol) + { + } + + // protocol + + WString GetExecutablePath() override + { + return targetProtocol->GetExecutablePath(); + } + + void Initialize(IGuiRemoteProtocolEvents* _events) override + { + events = _events; + targetProtocol->Initialize(_events); + } + + void Submit(bool& disconnected) override + { + targetProtocol->Submit(disconnected); + } + + void ProcessRemoteEvents() override + { + targetProtocol->ProcessRemoteEvents(); + } + }; + +/*********************************************************************** +Passing through +***********************************************************************/ + + class GuiRemoteEventCombinator_PassingThrough : public GuiRemoteEventCombinator + { + public: +#define EVENT_NOREQ(NAME, REQUEST) void On ## NAME() override { this->targetEvents->On ## NAME(); } +#define EVENT_REQ(NAME, REQUEST) void On ## NAME(const REQUEST& arguments) override { this->targetEvents->On ## NAME(arguments); } +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_REQ +#undef EVENT_NOREQ + +#define MESSAGE_NORES(NAME, RESPONSE) +#define MESSAGE_RES(NAME, RESPONSE) void Respond ## NAME(vint id, const RESPONSE& arguments) override { this->targetEvents->Respond ## NAME(id, arguments); } +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_RES +#undef MESSAGE_NORES + }; + + template + class GuiRemoteProtocolCombinator_PassingThrough : public GuiRemoteProtocolCombinator + { + public: + GuiRemoteProtocolCombinator_PassingThrough(IGuiRemoteProtocol* _protocol) + : GuiRemoteProtocolCombinator(_protocol) + { + } + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME() override { this->targetProtocol->Request ## NAME(); } +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id) override { this->targetProtocol->Request ## NAME(id); } +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME(const REQUEST& arguments) override { this->targetProtocol->Request ## NAME(arguments); } +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id, const REQUEST& arguments) override { this->targetProtocol->Request ## NAME(id, arguments); } +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES + }; +} + +#endif + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_CHANNEL_SHARED.H +***********************************************************************/ +/*********************************************************************** +Vczh Library++ 3.0 +Developer: Zihan Chen(vczh) +GacUI::Remote Window + +Interfaces: + IGuiRemoteProtocolChannel + +***********************************************************************/ + +#ifndef VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_CHANNEL +#define VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_CHANNEL + + +namespace vl::presentation::remoteprotocol::channeling +{ + +/*********************************************************************** +IGuiRemoteProtocolChannel +***********************************************************************/ + + template + class IGuiRemoteProtocolChannelReceiver : public virtual Interface + { + public: + virtual void OnReceive(const TPackage& package) = 0; + }; + + template + class IGuiRemoteProtocolChannel : public virtual Interface + { + public: + virtual void Initialize(IGuiRemoteProtocolChannelReceiver* receiver) = 0; + virtual IGuiRemoteProtocolChannelReceiver* GetReceiver() = 0; + virtual void Write(const TPackage& package) = 0; + virtual WString GetExecutablePath() = 0; + virtual void Submit(bool& disconnected) = 0; + virtual void ProcessRemoteEvents() = 0; + }; + +/*********************************************************************** +Serialization +***********************************************************************/ + + template + class GuiRemoteProtocolChannelTransformerBase + : public Object + , public virtual IGuiRemoteProtocolChannel + , protected virtual IGuiRemoteProtocolChannelReceiver + { + protected: + IGuiRemoteProtocolChannel* channel = nullptr; + IGuiRemoteProtocolChannelReceiver* receiver = nullptr; + + public: + GuiRemoteProtocolChannelTransformerBase(IGuiRemoteProtocolChannel* _channel) + : channel(_channel) + { + } + + void Initialize(IGuiRemoteProtocolChannelReceiver* _receiver) override + { + receiver = _receiver; + channel->Initialize(this); + } + + IGuiRemoteProtocolChannelReceiver* GetReceiver() override + { + return receiver; + } + + WString GetExecutablePath() override + { + return channel->GetExecutablePath(); + } + + void Submit(bool& disconnected) override + { + channel->Submit(disconnected); + } + + void ProcessRemoteEvents() override + { + channel->ProcessRemoteEvents(); + } + }; + + template + class GuiRemoteProtocolChannelSerializer + : public GuiRemoteProtocolChannelTransformerBase + { + protected: + typename TSerialization::ContextType context; + + void OnReceive(const typename TSerialization::DestType& package) override + { + typename TSerialization::SourceType deserialized; + TSerialization::Deserialize(context, package, deserialized); + this->receiver->OnReceive(deserialized); + } + + public: + GuiRemoteProtocolChannelSerializer(IGuiRemoteProtocolChannel* _channel, const typename TSerialization::ContextType& _context = {}) + : GuiRemoteProtocolChannelTransformerBase(_channel) + , context(_context) + { + } + + void Write(const typename TSerialization::SourceType& package) override + { + typename TSerialization::DestType serialized; + TSerialization::Serialize(context, package, serialized); + this->channel->Write(serialized); + } + }; + + template + class GuiRemoteProtocolChannelDeserializer + : public GuiRemoteProtocolChannelTransformerBase + { + protected: + typename TSerialization::ContextType context; + + void OnReceive(const typename TSerialization::SourceType& package) override + { + typename TSerialization::DestType serialized; + TSerialization::Serialize(context, package, serialized); + this->receiver->OnReceive(serialized); + } + + public: + GuiRemoteProtocolChannelDeserializer(IGuiRemoteProtocolChannel* _channel, const typename TSerialization::ContextType& _context = {}) + : GuiRemoteProtocolChannelTransformerBase(_channel) + , context(_context) + { + } + + void Write(const typename TSerialization::DestType& package) override + { + typename TSerialization::SourceType deserialized; + TSerialization::Deserialize(context, package, deserialized); + this->channel->Write(deserialized); + } + }; + +/*********************************************************************** +String Transformation +***********************************************************************/ + + template + static void ConvertUtfString(const ObjectString& source, ObjectString& dest) + { + vint len = _utftoutf(source.Buffer(), nullptr, 0); + if (len < 1) dest = {}; + TTo* buffer = new TTo[len]; + memset(buffer, 0, len * sizeof(TTo)); + _utftoutf(source.Buffer(), buffer, len); + dest = ObjectString::TakeOver(buffer, len - 1); + } + + template + struct UtfStringSerializer + { + using SourceType = ObjectString; + using DestType = ObjectString; + using ContextType = std::nullptr_t; + + static void Serialize(const ContextType&, const SourceType& source, DestType& dest) + { + ConvertUtfString(source, dest); + } + + static void Deserialize(const ContextType&, const DestType& source, SourceType& dest) + { + ConvertUtfString(source, dest); + } + }; + + template + using GuiRemoteUtfStringChannelSerializer = GuiRemoteProtocolChannelSerializer>; + + template + using GuiRemoteUtfStringChannelDeserializer = GuiRemoteProtocolChannelDeserializer>; +} + +#endif + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_CHANNEL_ASYNC.H +***********************************************************************/ +/*********************************************************************** +Vczh Library++ 3.0 +Developer: Zihan Chen(vczh) +GacUI::Remote Window + +Interfaces: + IGuiRemoteProtocolChannel + +***********************************************************************/ + +#ifndef VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_CHANNEL_ASYNC +#define VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_CHANNEL_ASYNC + + +namespace vl::presentation::remoteprotocol::channeling +{ + +/*********************************************************************** +Metadata +***********************************************************************/ + + enum class ChannelPackageSemantic + { + Message, + Request, + Response, + Event, + Unknown, + }; + + enum class ChannelAsyncState + { + Ready, + Running, + Stopped, + }; + +/*********************************************************************** +Async + A certain package type could run in async mode + if the following function is defined + and accessible via argument-dependent lookup + +void ChannelPackageSemanticUnpack( + const T& package, + ChannelPackageSemantic& semantic, + vint& id, + WString& name + ); +***********************************************************************/ + + class GuiRemoteProtocolAsyncChannelSerializerBase : public Object + { + public: + using TTaskProc = Func; + + private: + collections::List channelThreadTasks; + SpinLock channelThreadLock; + collections::List uiThreadTasks; + SpinLock uiThreadLock; + + protected: + void QueueTask(SpinLock& lock, collections::List& tasks, TTaskProc task, EventObject* signalAfterQueue); + void QueueTaskAndWait(SpinLock& lock, collections::List& tasks, TTaskProc task, EventObject* signalAfterQueue); + void FetchTasks(SpinLock& lock, collections::List& tasks, collections::List& results); + void FetchAndExecuteTasks(SpinLock& lock, collections::List& tasks); + + void FetchAndExecuteChannelTasks(); + void FetchAndExecuteUITasks(); + + void QueueToChannelThread(TTaskProc task, EventObject* signalAfterQueue); + void QueueToChannelThreadAndWait(TTaskProc task, EventObject* signalAfterQueue); + void QueueToUIThread(TTaskProc task, EventObject* signalAfterQueue); + void QueueToUIThreadAndWait(TTaskProc task, EventObject* signalAfterQueue); + + public: + GuiRemoteProtocolAsyncChannelSerializerBase(); + ~GuiRemoteProtocolAsyncChannelSerializerBase(); + }; + + template + class GuiRemoteProtocolAsyncChannelSerializer + : public GuiRemoteProtocolAsyncChannelSerializerBase + , public virtual IGuiRemoteProtocolChannel + , protected virtual IGuiRemoteProtocolChannelReceiver + { + static_assert( + std::is_same_v(), + std::declval(), + std::declval(), + std::declval() + ))>, + "ChannelPackageSemanticUnpack must be defined for this TPackage" + ); + + public: + using TChannelThreadProc = Func; + using TUIThreadProc = Func; + using TStartingProc = Func; + using TStoppingProc = Func; + using TUIMainProc = Func*)>; + + protected: + struct PendingRequestGroup + { + vint connectionCounter = -1; + collections::List requestIds; + }; + + IGuiRemoteProtocolChannel* channel = nullptr; + IGuiRemoteProtocolChannelReceiver* receiver = nullptr; + TUIMainProc uiMainProc; + collections::List uiPendingPackages; + + SpinLock lockEvents; + collections::List queuedEvents; + + SpinLock lockResponses; + EventObject eventAutoResponses; + collections::Dictionary queuedResponses; + Ptr pendingRequest; + + SpinLock lockConnection; + volatile vint connectionCounter = 0; + volatile bool connectionAvailable = false; + + volatile bool started = false; + volatile bool stopping = false; + volatile bool stopped = false; + Nullable executablePath; + + EventObject eventAutoChannelTaskQueued; + EventObject eventManualChannelThreadStopped; + EventObject eventManualUIThreadStopped; + + void UIThreadProc() + { + uiMainProc(this); + uiMainProc = {}; + + // Signal and wait for ChannelThreadProc to finish + stopping = true; + eventAutoChannelTaskQueued.Signal(); + eventManualChannelThreadStopped.Wait(); + + // All remaining queued callbacks should be executed + FetchAndExecuteUITasks(); + eventManualUIThreadStopped.Signal(); + } + + void ChannelThreadProc() + { + // TODO: + // The current version always start a channel thread + // So that it does not matter whether the underlying IO is sync or async + // But async IO does not need a channel thread + // Refactor and optimize the channel thread to be optional in the future + + // All members of "_channel" argument to Start is called in this thread + // So that the implementation does not need to care about thread safety + + // The thread stopped after receiving a signal from UIThreadProc + while (!stopping) + { + eventAutoChannelTaskQueued.Wait(); + FetchAndExecuteChannelTasks(); + } + + // All remaining queued callbacks should be executed + FetchAndExecuteChannelTasks(); + eventManualChannelThreadStopped.Signal(); + } + + protected: + + bool AreCurrentPendingRequestGroupSatisfied(bool disconnected) + { + if (!pendingRequest) return false; + if (disconnected) return true; + for (vint requestId : pendingRequest->requestIds) + { + if (!queuedResponses.Keys().Contains(requestId)) + { + return false; + } + } + return true; + } + + void OnReceive(const TPackage& package) override + { + // Called from any thread, very likely the channel thread +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::channeling::GuiRemoteProtocolAsyncChannelSerializer::OnReceive(...)#" + // If it is a response, unblock Submit() + // If it is an event, send to ProcessRemoteEvents() + + auto semantic = ChannelPackageSemantic::Unknown; + vint id = -1; + WString name; + ChannelPackageSemanticUnpack(package, semantic, id, name); + + switch (semantic) + { + case ChannelPackageSemantic::Event: + { + SPIN_LOCK(lockEvents) + { + queuedEvents.Add(package); + } + } + break; + case ChannelPackageSemantic::Response: + { + SPIN_LOCK(lockResponses) + { + queuedResponses.Add(id, package); + if (AreCurrentPendingRequestGroupSatisfied(false)) + { + eventAutoResponses.Signal(); + } + } + } + break; + default: + CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Only responses and events are expected."); + } + +#undef ERROR_MESSAGE_PREFIX + } + + public: + + void Write(const TPackage& package) override + { + // Called from UI thread + uiPendingPackages.Add(package); + } + + void Submit(bool& disconnected) override + { + // Called from UI thread +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::channeling::GuiRemoteProtocolAsyncChannelSerializer::Submit(...)#" + + SPIN_LOCK(lockConnection) + { + if (!connectionAvailable) + { + disconnected = true; + uiPendingPackages.Clear(); + return; + } + } + + // Group all pending requests into a group + auto requestGroup = Ptr(new PendingRequestGroup); + requestGroup->connectionCounter = connectionCounter; + for (auto&& package : uiPendingPackages) + { + auto semantic = ChannelPackageSemantic::Unknown; + vint id = -1; + WString name; + ChannelPackageSemanticUnpack(package, semantic, id, name); + + if (semantic == ChannelPackageSemantic::Request) + { + requestGroup->requestIds.Add(id); + } + } + SPIN_LOCK(lockResponses) + { + CHECK_ERROR(!pendingRequest, ERROR_MESSAGE_PREFIX L"Internal error."); + pendingRequest = requestGroup; + } + + QueueToChannelThread([this, requestGroup, packages = std::move(uiPendingPackages)]() + { + for (auto&& package : packages) + { + channel->Write(package); + } + bool disconnected = false; + channel->Submit(disconnected); + if (disconnected) + { + SPIN_LOCK(lockConnection) + { + if (requestGroup->connectionCounter == connectionCounter) + { + connectionAvailable = false; + } + } + } + + if (disconnected || requestGroup->requestIds.Count() == 0) + { + eventAutoResponses.Signal(); + } + }, &eventAutoChannelTaskQueued); + + // Block until the all responses of the top request group are received + // Re-entrance recursively is possible + eventAutoResponses.Wait(); + SPIN_LOCK(lockConnection) + { + if (requestGroup->connectionCounter != connectionCounter || !connectionAvailable) + { + disconnected = true; + } + } + + collections::List responses; + SPIN_LOCK(lockResponses) + { + if (!disconnected) + { + for (vint id : requestGroup->requestIds) + { + responses.Add(queuedResponses[id]); + queuedResponses.Remove(id); + } + } + pendingRequest = nullptr; + queuedResponses.Clear(); + } + + for (auto&& response : responses) + { + receiver->OnReceive(response); + } + +#undef ERROR_MESSAGE_PREFIX + } + + void ProcessRemoteEvents() override + { + // Called from UI thread + QueueToChannelThread([this]() + { + channel->ProcessRemoteEvents(); + }, &eventAutoChannelTaskQueued); + + FetchAndExecuteUITasks(); + + // Process of queued events from channel + collections::List events; + SPIN_LOCK(lockEvents) + { + events = std::move(queuedEvents); + } + + for (auto&& event : events) + { + { + auto semantic = ChannelPackageSemantic::Unknown; + vint id = -1; + WString name; + ChannelPackageSemanticUnpack(event, semantic, id, name); + + if (name == L"ControllerConnect") + { + SPIN_LOCK(lockConnection) + { + connectionCounter++; + connectionAvailable = true; + } + } + } + receiver->OnReceive(event); + } + } + + public: + + /// + /// Start the async channel. + /// + /// + /// A channel object that runs in the argument offered to startingProc. + /// + /// + /// A callback that runs in the argument offered to startingProc, which is supposed to call . + /// An example of argument to would be + /// over + /// over + /// over + /// + /// A callback executed in the current thread, that responsible to start two threads for arguments and . + /// + void Start( + IGuiRemoteProtocolChannel* _channel, + TUIMainProc _uiMainProc, + TStartingProc startingProc + ) + { +#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::channeling::GuiRemoteProtocolAsyncChannelSerializer::Start(...)#" + CHECK_ERROR(!started, ERROR_MESSAGE_PREFIX L"This function can only be called once."); + + channel = _channel; + uiMainProc = _uiMainProc; + + TChannelThreadProc thread_channel = [this]() + { + ChannelThreadProc(); + }; + + TUIThreadProc thread_ui = [this]() + { + UIThreadProc(); + }; + + eventAutoResponses.CreateAutoUnsignal(false); + eventAutoChannelTaskQueued.CreateAutoUnsignal(false); + eventManualChannelThreadStopped.CreateManualUnsignal(false); + eventManualUIThreadStopped.CreateManualUnsignal(false); + startingProc(thread_channel, thread_ui); + started = true; + +#undef ERROR_MESSAGE_PREFIX + } + + ChannelAsyncState GetAsyncStateUnsafe() + { + if (started) + { + if (stopped) + { + return ChannelAsyncState::Stopped; + } + else + { + return ChannelAsyncState::Running; + } + } + else + { + return ChannelAsyncState::Ready; + } + } + + void WaitForStopped() + { + eventManualUIThreadStopped.Wait(); + } + + public: + + GuiRemoteProtocolAsyncChannelSerializer() = default; + ~GuiRemoteProtocolAsyncChannelSerializer() = default; + + void ExecuteInChannelThread(TTaskProc task) + { + QueueToChannelThread(task, &eventAutoChannelTaskQueued); + } + + void Initialize(IGuiRemoteProtocolChannelReceiver* _receiver) override + { + // Called from UI thread + receiver = _receiver; + QueueToChannelThreadAndWait([this]() + { + channel->Initialize(this); + }, &eventAutoChannelTaskQueued); + } + + IGuiRemoteProtocolChannelReceiver* GetReceiver() override + { + // Called from UI thread + return receiver; + } + + WString GetExecutablePath() override + { + // Called from UI thread + if (!executablePath) + { + QueueToChannelThreadAndWait([this]() + { + executablePath = channel->GetExecutablePath(); + }, &eventAutoChannelTaskQueued); + } + return executablePath.Value(); + } + }; +} + +#endif + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_CHANNEL_JSON.H +***********************************************************************/ +/*********************************************************************** +Vczh Library++ 3.0 +Developer: Zihan Chen(vczh) +GacUI::Remote Window + +Interfaces: + IGuiRemoteProtocolChannel + +***********************************************************************/ + +#ifndef VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_CHANNEL_JSON +#define VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_CHANNEL_JSON + + +namespace vl::presentation::remoteprotocol::channeling +{ + using IJsonChannelReceiver = IGuiRemoteProtocolChannelReceiver>; + using IJsonChannel = IGuiRemoteProtocolChannel>; + +/*********************************************************************** +ChannelPackageSemantic +***********************************************************************/ + + extern void ChannelPackageSemanticUnpack(Ptr package, ChannelPackageSemantic& semantic, vint& id, WString& name); + +/*********************************************************************** +GuiRemoteProtocolFromJsonChannel +***********************************************************************/ + + class GuiRemoteProtocolFromJsonChannel + : public Object + , public virtual IGuiRemoteProtocol + , protected IJsonChannelReceiver + { + protected: + IJsonChannel* channel = nullptr; + IGuiRemoteProtocolEvents* events = nullptr; + + void OnReceive(const Ptr& package) override; + + public: + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME() override; +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id) override; +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME(const REQUEST& arguments) override; +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id, const REQUEST& arguments) override; +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES + + GuiRemoteProtocolFromJsonChannel(IJsonChannel* _channel); + ~GuiRemoteProtocolFromJsonChannel(); + + void Initialize(IGuiRemoteProtocolEvents* _events) override; + WString GetExecutablePath() override; + void Submit(bool& disconnected) override; + void ProcessRemoteEvents() override; + }; + +/*********************************************************************** +GuiRemoteJsonChannelFromProtocol +***********************************************************************/ + + class GuiRemoteJsonChannelFromProtocol + : public Object + , public virtual IJsonChannel + , protected virtual IGuiRemoteProtocolEvents + { + protected: + IJsonChannelReceiver* receiver = nullptr; + IGuiRemoteProtocol* protocol = nullptr; + +#define EVENT_NOREQ(NAME, REQUEST) void On ## NAME() override; +#define EVENT_REQ(NAME, REQUEST) void On ## NAME(const REQUEST& arguments) override; +#define EVENT_HANDLER(NAME, REQUEST, REQTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST) + GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) +#undef EVENT_HANDLER +#undef EVENT_REQ +#undef EVENT_NOREQ + +#define MESSAGE_NORES(NAME, RESPONSE) +#define MESSAGE_RES(NAME, RESPONSE) void Respond ## NAME(vint id, const RESPONSE& arguments) override; +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_RES +#undef MESSAGE_NORES + public: + + GuiRemoteJsonChannelFromProtocol(IGuiRemoteProtocol* _protocol); + ~GuiRemoteJsonChannelFromProtocol(); + + void Initialize(IJsonChannelReceiver* _receiver) override; + IJsonChannelReceiver* GetReceiver() override; + void Write(const Ptr& package) override; + WString GetExecutablePath() override; + void Submit(bool& disconnected) override; + void ProcessRemoteEvents() override; + }; + +/*********************************************************************** +JsonToStringSerializer +***********************************************************************/ + + struct JsonToStringSerializer + { + using SourceType = Ptr; + using DestType = WString; + using ContextType = Ptr; + + static void Serialize(Ptr parser, const SourceType& source, DestType& dest); + static void Deserialize(Ptr parser, const DestType& source, SourceType& dest); + }; + + using GuiRemoteJsonChannelStringSerializer = GuiRemoteProtocolChannelSerializer; + using GuiRemoteJsonChannelStringDeserializer = GuiRemoteProtocolChannelDeserializer; } #endif @@ -23336,41 +24254,16 @@ GuiRemoteEventFilterVerifier public: bool submitting = false; - void ClearDropRepeatMasks() - { -#define EVENT_NODROP(NAME) -#define EVENT_DROPREP(NAME) lastDropRepeatEvent ## NAME = false; -#define EVENT_DROPCON(NAME) -#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## DROPTAG(NAME) - GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) -#undef EVENT_HANDLER -#undef EVENT_DROPCON -#undef EVENT_DROPREP -#undef EVENT_NODROP - } + GuiRemoteEventFilterVerifier(); + ~GuiRemoteEventFilterVerifier(); - void ClearDropConsecutiveMasks() - { -#define EVENT_NODROP(NAME) -#define EVENT_DROPREP(NAME) -#define EVENT_DROPCON(NAME) lastDropConsecutiveEvent ## NAME = false; -#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## DROPTAG(NAME) - GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) -#undef EVENT_HANDLER -#undef EVENT_DROPCON -#undef EVENT_DROPREP -#undef EVENT_NODROP - } + void ClearDropRepeatMasks(); + void ClearDropConsecutiveMasks(); // responses #define MESSAGE_NORES(NAME, RESPONSE) -#define MESSAGE_RES(NAME, RESPONSE)\ - void Respond ## NAME(vint id, const RESPONSE& arguments) override\ - {\ - targetEvents->Respond ## NAME(id, arguments);\ - }\ - +#define MESSAGE_RES(NAME, RESPONSE) void Respond ## NAME(vint id, const RESPONSE& arguments) override; #define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) #undef MESSAGE_HANDLER @@ -23378,54 +24271,13 @@ GuiRemoteEventFilterVerifier #undef MESSAGE_NORES // events - -#define EVENT_NODROP(NAME) - -#define EVENT_DROPREP(NAME)\ - CHECK_ERROR(!lastDropRepeatEvent ## NAME, L"vl::presentation::remoteprotocol::GuiRemoteEventFilterVerifier::On" L ## #NAME L"(...)#[@DropRepeat] event repeated.");\ - lastDropRepeatEvent ## NAME = true;\ - -#define EVENT_DROPCON(NAME)\ - CHECK_ERROR(!lastDropConsecutiveEvent ## NAME, L"vl::presentation::remoteprotocol::GuiRemoteEventFilterVerifier::On" L ## #NAME L"(...)#[@DropConsecutive] event repeated.");\ - ClearDropConsecutiveMasks();\ - lastDropConsecutiveEvent ## NAME = true;\ - -#define EVENT_NOREQ(NAME, REQUEST, DROPTAG)\ - void On ## NAME() override\ - {\ - if (submitting)\ - {\ - EVENT_ ## DROPTAG(NAME);\ - targetEvents->On ## NAME();\ - }\ - else\ - {\ - targetEvents->On ## NAME();\ - }\ - }\ - -#define EVENT_REQ(NAME, REQUEST, DROPTAG)\ - void On ## NAME(const REQUEST& arguments) override\ - {\ - if (submitting)\ - {\ - EVENT_ ## DROPTAG(NAME);\ - targetEvents->On ## NAME(arguments);\ - }\ - else\ - {\ - targetEvents->On ## NAME(arguments);\ - }\ - }\ - +#define EVENT_NOREQ(NAME, REQUEST, DROPTAG) void On ## NAME() override; +#define EVENT_REQ(NAME, REQUEST, DROPTAG) void On ## NAME(const REQUEST& arguments) override; #define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST, DROPTAG) GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) #undef EVENT_HANDLER #undef EVENT_REQ #undef EVENT_NOREQ -#undef EVENT_DROPCON -#undef EVENT_DROPREP -#undef EVENT_NOREP }; /*********************************************************************** @@ -23438,7 +24290,7 @@ GuiRemoteProtocolFilterVerifier { friend class GuiRemoteProtocolFilter; protected: - vint lastRequestId = -1; + vint lastRequestId = -1; #define MESSAGE_NODROP(NAME) #define MESSAGE_DROPREP(NAME) bool lastDropRepeatRequest ## NAME = false; @@ -23448,62 +24300,20 @@ GuiRemoteProtocolFilterVerifier #undef MESSAGE_DROPREP #undef MESSAGE_NODROP - void ClearDropRepeatMasks() - { -#define MESSAGE_NODROP(NAME) -#define MESSAGE_DROPREP(NAME) lastDropRepeatRequest ## NAME = false; -#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG) MESSAGE_ ## DROPTAG(NAME) - GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) -#undef MESSAGE_HANDLER -#undef MESSAGE_DROPREP -#undef MESSAGE_NODROP - } + void ClearDropRepeatMasks(); + public: - GuiRemoteProtocolFilterVerifier(IGuiRemoteProtocol* _protocol) - : GuiRemoteProtocolCombinator(_protocol) - { - } - - protected: + GuiRemoteProtocolFilterVerifier(IGuiRemoteProtocol* _protocol); + ~GuiRemoteProtocolFilterVerifier(); public: // messages -#define MESSAGE_NODROP(NAME) - -#define MESSAGE_DROPREP(NAME)\ - CHECK_ERROR(!lastDropRepeatRequest ## NAME, L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilterVerifier::Request" L ## #NAME L"(...)#[@DropRepeat] message repeated.");\ - lastDropRepeatRequest ## NAME = true;\ - -#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME() override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - targetProtocol->Request ## NAME();\ - }\ - -#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME(vint id) override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - targetProtocol->Request ## NAME(id);\ - }\ - -#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME(const REQUEST& arguments) override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - targetProtocol->Request ## NAME(arguments);\ - }\ - -#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME(vint id, const REQUEST& arguments) override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - targetProtocol->Request ## NAME(id, arguments);\ - }\ - +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME() override; +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME(vint id) override; +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME(const REQUEST& arguments) override; +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME(vint id, const REQUEST& arguments) override; #define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE, DROPTAG) GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) #undef MESSAGE_HANDLER @@ -23511,23 +24321,10 @@ GuiRemoteProtocolFilterVerifier #undef MESSAGE_REQ_NORES #undef MESSAGE_NOREQ_RES #undef MESSAGE_NOREQ_NORES -#undef MESSAGE_DROPREP -#undef MESSAGE_NODROP // protocol - void Submit() override - { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::repeatfiltering::GuiRemoteProtocolFilterVerifier::Submit()#" - CHECK_ERROR(!eventCombinator.submitting, ERROR_MESSAGE_PREFIX L"This function is not allowed to be called recursively."); - eventCombinator.submitting = true; - GuiRemoteProtocolCombinator::Submit(); - ClearDropRepeatMasks(); - eventCombinator.ClearDropRepeatMasks(); - eventCombinator.ClearDropConsecutiveMasks(); - eventCombinator.submitting = false; -#undef ERROR_MESSAGE_PREFIX - } + void Submit(bool& disconnected) override; }; } @@ -23644,95 +24441,17 @@ GuiRemoteEventFilter public: bool submitting = false; collections::Dictionary responseIds; + + GuiRemoteEventFilter(); + ~GuiRemoteEventFilter(); - void ProcessResponses() - { - for (auto&& response : filteredResponses) - { -#define MESSAGE_NORES(NAME, RESPONSE) -#define MESSAGE_RES(NAME, RESPONSE)\ - case FilteredResponseNames::NAME:\ - targetEvents->Respond ## NAME(response.id, response.arguments.Get());\ - break;\ - -#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) - switch (response.name) - { - GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) - default: - CHECK_FAIL(L"vl::presentation::remoteprotocol::GuiRemoteEventFilter::ProcessResponses()#Unrecognized response."); - } -#undef MESSAGE_HANDLER -#undef MESSAGE_RES -#undef MESSAGE_NORES - } - - filteredResponses.Clear(); - } - - void ProcessEvents() - { -#define EVENT_NODROP(NAME) -#define EVENT_DROPREP(NAME) lastDropRepeatEvent ## NAME = -1; -#define EVENT_DROPCON(NAME) lastDropConsecutiveEvent ## NAME = -1; -#define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## DROPTAG(NAME) - GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) -#undef EVENT_HANDLER -#undef EVENT_DROPCON -#undef EVENT_DROPREP -#undef EVENT_NODROP - - collections::List events(std::move(filteredEvents)); - - for (auto&& event : events) - { - if (event.dropped) - { - continue; - } - -#define EVENT_NOREQ(NAME, REQUEST)\ - case FilteredEventNames::NAME:\ - targetEvents->On ## NAME();\ - break;\ - -#define EVENT_REQ(NAME, REQUEST)\ - case FilteredEventNames::NAME:\ - targetEvents->On ## NAME(event.arguments.Get());\ - break;\ - -#define EVENT_HANDLER(NAME, REQUEST, REQTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST) - switch (event.name) - { - GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) - default: - CHECK_FAIL(L"vl::presentation::remoteprotocol::GuiRemoteEventFilter::ProcessEvents()#Unrecognized event."); - } -#undef EVENT_HANDLER -#undef EVENT_REQ -#undef EVENT_NOREQ - } - } + void ProcessResponses(); + void ProcessEvents(); // responses #define MESSAGE_NORES(NAME, RESPONSE) -#define MESSAGE_RES(NAME, RESPONSE)\ - void Respond ## NAME(vint id, const RESPONSE& arguments) override\ - {\ - CHECK_ERROR(\ - responseIds[id] == FilteredResponseNames::NAME,\ - L"vl::presentation::remoteprotocol::GuiRemoteEventFilter::"\ - L"Respond" L ## #NAME L"()#"\ - L"Messages sending to IGuiRemoteProtocol should be responded by calling the correct function.");\ - responseIds.Remove(id);\ - FilteredResponse response;\ - response.id = id;\ - response.name = FilteredResponseNames::NAME;\ - response.arguments = arguments;\ - filteredResponses.Add(response);\ - }\ - +#define MESSAGE_RES(NAME, RESPONSE) void Respond ## NAME(vint id, const RESPONSE& arguments) override; #define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## RESTAG(NAME, RESPONSE) GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) #undef MESSAGE_HANDLER @@ -23740,64 +24459,14 @@ GuiRemoteEventFilter #undef MESSAGE_NORES // events - -#define EVENT_NODROP(NAME) - -#define EVENT_DROPREP(NAME)\ - if (lastDropRepeatEvent ## NAME != -1)\ - {\ - filteredEvents[lastDropRepeatEvent ## NAME].dropped = true;\ - }\ - lastDropRepeatEvent ## NAME = filteredEvents.Count() - 1\ - -#define EVENT_DROPCON(NAME)\ - if (lastDropConsecutiveEvent ## NAME != -1 && lastDropConsecutiveEvent ## NAME == filteredEvents.Count() - 1)\ - {\ - filteredEvents[lastDropConsecutiveEvent ## NAME].dropped = true;\ - }\ - lastDropConsecutiveEvent ## NAME = filteredEvents.Count() - 1\ - -#define EVENT_NOREQ(NAME, REQUEST, DROPTAG)\ - void On ## NAME() override\ - {\ - if (submitting)\ - {\ - EVENT_ ## DROPTAG(NAME);\ - FilteredEvent event;\ - event.name = FilteredEventNames::NAME;\ - filteredEvents.Add(event);\ - }\ - else\ - {\ - targetEvents->On ## NAME();\ - }\ - }\ - -#define EVENT_REQ(NAME, REQUEST, DROPTAG)\ - void On ## NAME(const REQUEST& arguments) override\ - {\ - if (submitting)\ - {\ - EVENT_ ## DROPTAG(NAME);\ - FilteredEvent event;\ - event.name = FilteredEventNames::NAME;\ - event.arguments = arguments;\ - filteredEvents.Add(event);\ - }\ - else\ - {\ - targetEvents->On ## NAME(arguments);\ - }\ - }\ - + +#define EVENT_NOREQ(NAME, REQUEST, DROPTAG) void On ## NAME() override; +#define EVENT_REQ(NAME, REQUEST, DROPTAG) void On ## NAME(const REQUEST& arguments) override; #define EVENT_HANDLER(NAME, REQUEST, REQTAG, DROPTAG, ...) EVENT_ ## REQTAG(NAME, REQUEST, DROPTAG) GACUI_REMOTEPROTOCOL_EVENTS(EVENT_HANDLER) #undef EVENT_HANDLER #undef EVENT_REQ #undef EVENT_NOREQ -#undef EVENT_DROPCON -#undef EVENT_DROPREP -#undef EVENT_NOREP }; /*********************************************************************** @@ -23807,8 +24476,8 @@ GuiRemoteProtocolFilter class GuiRemoteProtocolFilter : public GuiRemoteProtocolCombinator { protected: - vint lastRequestId = -1; - collections::List filteredRequests; + vint lastRequestId = -1; + collections::List filteredRequests; #define MESSAGE_NODROP(NAME) #define MESSAGE_DROPREP(NAME) vint lastDropRepeatRequest ## NAME = -1; @@ -23817,140 +24486,23 @@ GuiRemoteProtocolFilter #undef MESSAGE_HANDLER #undef MESSAGE_DROPREP #undef MESSAGE_NODROP - - void ProcessRequests() - { -#define MESSAGE_NODROP(NAME) -#define MESSAGE_DROPREP(NAME) lastDropRepeatRequest ## NAME = -1; -#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG) MESSAGE_ ## DROPTAG(NAME) - GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) -#undef MESSAGE_HANDLER -#undef MESSAGE_DROPREP -#undef MESSAGE_NODROP - - for (auto&& request : filteredRequests) - { - CHECK_ERROR(\ - !request.dropped || request.id == -1,\ - L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::ProcessRequests()#"\ - L"Messages with id cannot be dropped.");\ - if (request.dropped) - { - continue; - } - -#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE)\ - case FilteredRequestNames::NAME:\ - targetProtocol->Request ## NAME();\ - break;\ - -#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE)\ - case FilteredRequestNames::NAME:\ - targetProtocol->Request ## NAME(request.id);\ - break;\ - -#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE)\ - case FilteredRequestNames::NAME:\ - targetProtocol->Request ## NAME(request.arguments.Get());\ - break;\ - -#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE)\ - case FilteredRequestNames::NAME:\ - targetProtocol->Request ## NAME(request.id, request.arguments.Get());\ - break;\ - -#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE) - switch (request.name) - { - GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) - default: - CHECK_FAIL(L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::ProcessRequests()#Unrecognized request."); - } -#undef MESSAGE_HANDLER -#undef MESSAGE_REQ_RES -#undef MESSAGE_REQ_NORES -#undef MESSAGE_NOREQ_RES -#undef MESSAGE_NOREQ_NORES - } - - CHECK_ERROR(eventCombinator.responseIds.Count() == 0, L"Messages sending to IGuiRemoteProtocol should be all responded."); - filteredRequests.Clear(); - } + + void ProcessRequests(); public: - GuiRemoteProtocolFilter(IGuiRemoteProtocol* _protocol) - : GuiRemoteProtocolCombinator(_protocol) - { - } + GuiRemoteProtocolFilter(IGuiRemoteProtocol* _protocol); + ~GuiRemoteProtocolFilter(); protected: public: // messages - -#define MESSAGE_NODROP(NAME) - -#define MESSAGE_DROPREP(NAME)\ - if (lastDropRepeatRequest ## NAME != -1)\ - {\ - filteredRequests[lastDropRepeatRequest ## NAME].dropped = true;\ - }\ - lastDropRepeatRequest ## NAME = filteredRequests.Count()\ - -#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME() override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - FilteredRequest request;\ - request.name = FilteredRequestNames::NAME;\ - filteredRequests.Add(request);\ - }\ - -#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME(vint id) override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - CHECK_ERROR(\ - lastRequestId < id,\ - L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::"\ - L"Request" L ## #NAME L"()#"\ - L"Id of a message sending to IGuiRemoteProtocol should be increasing.");\ - lastRequestId = id;\ - FilteredRequest request;\ - request.id = id;\ - request.name = FilteredRequestNames::NAME;\ - filteredRequests.Add(request);\ - eventCombinator.responseIds.Add(id, FilteredResponseNames::NAME);\ - }\ - -#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME(const REQUEST& arguments) override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - FilteredRequest request;\ - request.name = FilteredRequestNames::NAME;\ - request.arguments = arguments;\ - filteredRequests.Add(request);\ - }\ - -#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE, DROPTAG)\ - void Request ## NAME(vint id, const REQUEST& arguments) override\ - {\ - MESSAGE_ ## DROPTAG(NAME);\ - CHECK_ERROR(\ - lastRequestId < id,\ - L"vl::presentation::remoteprotocol::GuiRemoteProtocolFilter::"\ - L"Request" L ## #NAME L"()#"\ - L"Id of a message sending to IGuiRemoteProtocol should be increasing.");\ - lastRequestId = id;\ - FilteredRequest request;\ - request.id = id;\ - request.name = FilteredRequestNames::NAME;\ - request.arguments = arguments;\ - filteredRequests.Add(request);\ - eventCombinator.responseIds.Add(id, FilteredResponseNames::NAME);\ - }\ - + + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME() override; +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME(vint id) override; +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME(const REQUEST& arguments) override; +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE, DROPTAG) void Request ## NAME(vint id, const REQUEST& arguments) override; #define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, DROPTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE, DROPTAG) GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) #undef MESSAGE_HANDLER @@ -23958,37 +24510,291 @@ GuiRemoteProtocolFilter #undef MESSAGE_REQ_NORES #undef MESSAGE_NOREQ_RES #undef MESSAGE_NOREQ_NORES -#undef MESSAGE_DROPREP -#undef MESSAGE_NODROP - + // protocol - - void Initialize(IGuiRemoteProtocolEvents* _events) override + + void Initialize(IGuiRemoteProtocolEvents* _events) override; + void Submit(bool& disconnected) override; + }; +} + +#endif + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\PROTOCOL\FRAMEOPERATIONS\GUIREMOTEPROTOCOLSCHEMA_FRAMEOPERATIONS.H +***********************************************************************/ +/*********************************************************************** +Vczh Library++ 3.0 +Developer: Zihan Chen(vczh) +GacUI::Remote Window + +***********************************************************************/ + +#ifndef VCZH_PRESENTATION_GUIREMOTECONTROLLER_REMOTEPROTOCOLSCHEMA_FRAMEOPERATIONS +#define VCZH_PRESENTATION_GUIREMOTECONTROLLER_REMOTEPROTOCOLSCHEMA_FRAMEOPERATIONS + + +namespace vl::presentation::remoteprotocol +{ + /* + * dom id: + * + * root: -1 + * element: (elementId << 2) + 0 + * parent of element: (elementId << 2) + 1 + * hittest: (compositionId << 2) + 2 + * parent of hittest: (compositionId << 2) + 3 + */ + + class RenderingDomBuilder + { + using RenderingResultRef = Ptr; + using RenderingResultRefList = collections::List; + protected: + RenderingResultRefList domStack; + collections::List domBoundaries; + Ptr domRoot; + Ptr domCurrent; + + vint GetCurrentBoundary(); + vint Push(RenderingResultRef ref); + void PopTo(vint index); + void Pop(); + void PopBoundary(); + + template + void PrepareParentFromCommand(Rect commandBounds, Rect commandValidArea, vint newDomId, TCallback&& calculateValidAreaFromDom); + public: + RenderingDomBuilder() = default; + ~RenderingDomBuilder() = default; + + void RequestRendererBeginRendering(); + void RequestRendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments); + void RequestRendererEndBoundary(); + void RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments); + Ptr RequestRendererEndRendering(); + }; + + extern Ptr CopyDom(Ptr root); + + struct DomIndexItem + { + vint id; + vint parentId; + Ptr dom; + + auto operator<=>(const DomIndexItem&) const = default; + }; + using DomIndex = collections::Array; + + extern void BuildDomIndex(Ptr root, DomIndex& index); + extern void UpdateDomInplace(Ptr root, DomIndex& index, const RenderingDom_DiffsInOrder& diffs); + extern void DiffDom(Ptr domFrom, DomIndex& indexFrom, Ptr domTo, DomIndex& indexTo, RenderingDom_DiffsInOrder& diffs); +} + +#endif + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTERENDERER\GUIREMOTERENDERERSINGLE.H +***********************************************************************/ +/*********************************************************************** +Vczh Library++ 3.0 +Developer: Zihan Chen(vczh) +GacUI::Remote Window + +Interfaces: + GuiRemoteRendererSingle + +***********************************************************************/ + +#ifndef VCZH_PRESENTATION_GUIREMOTECONTROLLER_REMOTERENDERER_GUIREMOTERENDERERSINGLE +#define VCZH_PRESENTATION_GUIREMOTECONTROLLER_REMOTERENDERER_GUIREMOTERENDERERSINGLE + + +namespace vl::presentation::remote_renderer +{ + class GuiRemoteRendererSingle + : public Object + , public virtual IGuiRemoteProtocol + , protected virtual INativeWindowListener + , protected virtual INativeControllerListener + { + protected: + INativeWindow* window = nullptr; + INativeScreen* screen = nullptr; + IGuiRemoteProtocolEvents* events = nullptr; + bool disconnectingFromCore = false; + + bool updatingBounds = false; + remoteprotocol::WindowSizingConfig windowSizingConfig; + + remoteprotocol::ScreenConfig GetScreenConfig(INativeScreen* screen); + remoteprotocol::WindowSizingConfig GetWindowSizingConfig(); + void UpdateConfigsIfNecessary(); + + void NativeWindowDestroying(INativeWindow* _window) override; + + void Opened() override; + void BeforeClosing(bool& cancel) override; + void AfterClosing() override; + void Closed() override; + + void Moved() override; + void DpiChanged(bool preparing) override; + void RenderingAsActivated() override; + void RenderingAsDeactivated() override; + + protected: + struct SolidLabelMeasuring { - if (auto verifierProtocol = dynamic_cast(targetProtocol)) - { - verifierProtocol->targetProtocol->Initialize(&eventCombinator); - eventCombinator.targetEvents = &verifierProtocol->eventCombinator; - verifierProtocol->eventCombinator.targetEvents = _events; - } - else - { - GuiRemoteProtocolCombinator::Initialize(_events); - } - } + remoteprotocol::ElementSolidLabelMeasuringRequest request; + Nullable minSize; + }; + + using ElementMap = collections::Dictionary>; + using ImageMap = collections::Dictionary>; + using SolidLabelMeasuringMap = collections::Dictionary; + using FontHeightMeasuringSet = collections::SortedList>; + + remoteprotocol::ElementMeasurings elementMeasurings; + FontHeightMeasuringSet fontHeightMeasurings; + SolidLabelMeasuringMap solidLabelMeasurings; + + ElementMap availableElements; + ImageMap availableImages; + Ptr renderingDom; + remoteprotocol::DomIndex renderingDomIndex; + + Alignment GetAlignment(remoteprotocol::ElementHorizontalAlignment alignment); + Alignment GetAlignment(remoteprotocol::ElementVerticalAlignment alignment); + void StoreLabelMeasuring(vint id, remoteprotocol::ElementSolidLabelMeasuringRequest request, Ptr solidLabel, Size minSize); + remoteprotocol::ImageMetadata CreateImageMetadata(vint id, INativeImage* image); + remoteprotocol::ImageMetadata CreateImage(const remoteprotocol::ImageCreation& arguments); + void CheckDom(); + + protected: + bool supressPaint = false; + bool needRefresh = false; + + void UpdateRenderTarget(elements::IGuiGraphicsRenderTarget* rt); + void Render(Ptr dom, elements::IGuiGraphicsRenderTarget* rt); + void HitTestInternal(Ptr dom, Point location, Nullable& hitTestResult, Nullable& cursorType); + void HitTest(Ptr dom, Point location, INativeWindowListener::HitTestResult& hitTestResult, INativeCursor*& cursor); + + void GlobalTimer() override; + void Paint() override; + INativeWindowListener::HitTestResult HitTest(NativePoint location) override; + + protected: + + void LeftButtonDown(const NativeWindowMouseInfo& info) override; + void LeftButtonUp(const NativeWindowMouseInfo& info) override; + void LeftButtonDoubleClick(const NativeWindowMouseInfo& info) override; + void RightButtonDown(const NativeWindowMouseInfo& info) override; + void RightButtonUp(const NativeWindowMouseInfo& info) override; + void RightButtonDoubleClick(const NativeWindowMouseInfo& info) override; + void MiddleButtonDown(const NativeWindowMouseInfo& info) override; + void MiddleButtonUp(const NativeWindowMouseInfo& info) override; + void MiddleButtonDoubleClick(const NativeWindowMouseInfo& info) override; + void HorizontalWheel(const NativeWindowMouseInfo& info) override; + void VerticalWheel(const NativeWindowMouseInfo& info) override; + void MouseMoving(const NativeWindowMouseInfo& info) override; + void MouseEntered() override; + void MouseLeaved() override; + void KeyDown(const NativeWindowKeyInfo& info) override; + void KeyUp(const NativeWindowKeyInfo& info) override; + void Char(const NativeWindowCharInfo& info) override; + + public: + GuiRemoteRendererSingle(); + ~GuiRemoteRendererSingle(); + + void RegisterMainWindow(INativeWindow* _window); + void UnregisterMainWindow(); + void ForceExitByFatelError(); + + WString GetExecutablePath() override; + void Initialize(IGuiRemoteProtocolEvents* _events) override; + void Submit(bool& disconnected) override; + void ProcessRemoteEvents() override; + + +#define MESSAGE_NOREQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME() override; +#define MESSAGE_NOREQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id) override; +#define MESSAGE_REQ_NORES(NAME, REQUEST, RESPONSE) void Request ## NAME(const REQUEST& arguments) override; +#define MESSAGE_REQ_RES(NAME, REQUEST, RESPONSE) void Request ## NAME(vint id, const REQUEST& arguments) override; +#define MESSAGE_HANDLER(NAME, REQUEST, RESPONSE, REQTAG, RESTAG, ...) MESSAGE_ ## REQTAG ## _ ## RESTAG(NAME, REQUEST, RESPONSE) + GACUI_REMOTEPROTOCOL_MESSAGES(MESSAGE_HANDLER) +#undef MESSAGE_HANDLER +#undef MESSAGE_REQ_RES +#undef MESSAGE_REQ_NORES +#undef MESSAGE_NOREQ_RES +#undef MESSAGE_NOREQ_NORES + }; +} + +#endif + +/*********************************************************************** +.\PLATFORMPROVIDERS\REMOTE\GUIREMOTEPROTOCOL_DOMDIFF.H +***********************************************************************/ +/*********************************************************************** +Vczh Library++ 3.0 +Developer: Zihan Chen(vczh) +GacUI::Remote Window + +Interfaces: + IGuiRemoteProtocol + +***********************************************************************/ + +#ifndef VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_DOMDIFF +#define VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL_DOMDIFF + + +namespace vl::presentation::remoteprotocol +{ + +/*********************************************************************** +GuiRemoteEventDomDiffConverter +***********************************************************************/ + + class GuiRemoteProtocolDomDiffConverter; + + class GuiRemoteEventDomDiffConverter : public GuiRemoteEventCombinator_PassingThrough + { + friend class GuiRemoteProtocolDomDiffConverter; + using TBase = GuiRemoteEventCombinator_PassingThrough; + protected: + Ptr lastDom; + DomIndex lastDomIndex; + + public: + GuiRemoteEventDomDiffConverter(); + ~GuiRemoteEventDomDiffConverter(); + + void OnControllerConnect() override; + }; + +/*********************************************************************** +GuiRemoteProtocolDomDiffConverter +***********************************************************************/ - void Submit() override - { -#define ERROR_MESSAGE_PREFIX L"vl::presentation::remoteprotocol::repeatfiltering::GuiRemoteProtocolFilter::Submit()#" - CHECK_ERROR(!eventCombinator.submitting, ERROR_MESSAGE_PREFIX L"This function is not allowed to be called recursively."); - eventCombinator.submitting = true; - ProcessRequests(); - eventCombinator.ProcessResponses(); - GuiRemoteProtocolCombinator::Submit(); - eventCombinator.submitting = false; - eventCombinator.ProcessEvents(); -#undef ERROR_MESSAGE_PREFIX - } + class GuiRemoteProtocolDomDiffConverter : public GuiRemoteProtocolCombinator_PassingThrough + { + using TBase = GuiRemoteProtocolCombinator_PassingThrough; + protected: + RenderingDomBuilder renderingDomBuilder; + + public: + GuiRemoteProtocolDomDiffConverter(IGuiRemoteProtocol* _protocol); + ~GuiRemoteProtocolDomDiffConverter(); + + void RequestRendererBeginRendering(const remoteprotocol::ElementBeginRendering& arguments) override; + void RequestRendererEndRendering(vint id) override; + void RequestRendererBeginBoundary(const remoteprotocol::ElementBoundary& arguments) override; + void RequestRendererEndBoundary() override; + void RequestRendererRenderElement(const remoteprotocol::ElementRendering& arguments) override; }; } @@ -24011,6 +24817,11 @@ Interfaces: #define VCZH_PRESENTATION_GUIREMOTECONTROLLER_GUIREMOTEPROTOCOL +namespace vl::presentation::remoteprotocol::channeling +{ + using GuiRemoteProtocolAsyncJsonChannelSerializer = GuiRemoteProtocolAsyncChannelSerializer>; +} + #endif /*********************************************************************** @@ -25042,6 +25853,8 @@ extern int SetupWindowsGDIRenderer(); extern int SetupWindowsDirect2DRenderer(); extern int SetupHostedWindowsGDIRenderer(); extern int SetupHostedWindowsDirect2DRenderer(); +extern int SetupRawWindowsGDIRenderer(); +extern int SetupRawWindowsDirect2DRenderer(); // Gtk extern int SetupGtkRenderer(); @@ -28627,7 +29440,7 @@ GuiRemoteMessages GuiRemoteMessages(GuiRemoteController* _remote); ~GuiRemoteMessages(); - void Submit(); + void Submit(bool& disconnected); // messages diff --git a/Import/Metadata/RemoteProtocol.json b/Import/Metadata/RemoteProtocol.json index f912c5fc..7d789a26 100644 --- a/Import/Metadata/RemoteProtocol.json +++ b/Import/Metadata/RemoteProtocol.json @@ -2211,6 +2211,13 @@ "attributes": [], "name": "ElementBoundary", "members": [{ + "$ast": "StructMember", + "name": "id", + "type": { + "$ast": "PrimitiveType", + "type": "Integer" + } + }, { "$ast": "StructMember", "name": "hitTestResult", "type": { @@ -2383,42 +2390,10 @@ "name": "ElementMeasurings" } } - }, { - "$ast": "UnionDecl", - "attributes": [], - "name": "ElementDescVariant", - "members": [{ - "$ast": "UnionMember", - "name": "ElementDesc_SolidBorder" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_SinkBorder" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_SinkSplitter" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_SolidBackground" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_GradientBackground" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_InnerShadow" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_Polygon" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_SolidLabel" - }, { - "$ast": "UnionMember", - "name": "ElementDesc_ImageFrame" - }] }, { "$ast": "StructDecl", "attributes": [], - "name": "RenderingDom", + "name": "RenderingDomContent", "members": [{ "$ast": "StructMember", "name": "hitTestResult", @@ -2463,6 +2438,26 @@ "$ast": "ReferenceType", "name": "Rect" } + }], + "type": "Struct" + }, { + "$ast": "StructDecl", + "attributes": [], + "name": "RenderingDom", + "members": [{ + "$ast": "StructMember", + "name": "id", + "type": { + "$ast": "PrimitiveType", + "type": "Integer" + } + }, { + "$ast": "StructMember", + "name": "content", + "type": { + "$ast": "ReferenceType", + "name": "RenderingDomContent" + } }, { "$ast": "StructMember", "name": "children", @@ -2476,40 +2471,52 @@ }], "type": "Class" }, { - "$ast": "StructDecl", + "$ast": "EnumDecl", "attributes": [], - "name": "RenderingCommand_BeginBoundary", + "name": "RenderingDom_DiffType", "members": [{ - "$ast": "StructMember", - "name": "boundary", - "type": { - "$ast": "ReferenceType", - "name": "ElementBoundary" - } - }], - "type": "Struct" + "$ast": "EnumMember", + "name": "Deleted" + }, { + "$ast": "EnumMember", + "name": "Created" + }, { + "$ast": "EnumMember", + "name": "Modified" + }] }, { "$ast": "StructDecl", "attributes": [], - "name": "RenderingCommand_EndBoundary", - "members": [], - "type": "Struct" - }, { - "$ast": "StructDecl", - "attributes": [], - "name": "RenderingCommand_Element", + "name": "RenderingDom_Diff", "members": [{ "$ast": "StructMember", - "name": "rendering", + "name": "id", "type": { - "$ast": "ReferenceType", - "name": "ElementRendering" + "$ast": "PrimitiveType", + "type": "Integer" } }, { "$ast": "StructMember", - "name": "element", + "name": "diffType", + "type": { + "$ast": "ReferenceType", + "name": "RenderingDom_DiffType" + } + }, { + "$ast": "StructMember", + "name": "content", "type": { "$ast": "OptionalType", + "element": { + "$ast": "ReferenceType", + "name": "RenderingDomContent" + } + } + }, { + "$ast": "StructMember", + "name": "children", + "type": { + "$ast": "ArrayType", "element": { "$ast": "PrimitiveType", "type": "Integer" @@ -2517,24 +2524,82 @@ } }], "type": "Struct" + }, { + "$ast": "StructDecl", + "attributes": [], + "name": "RenderingDom_DiffsInOrder", + "members": [{ + "$ast": "StructMember", + "name": "diffsInOrder", + "type": { + "$ast": "ArrayType", + "element": { + "$ast": "ReferenceType", + "name": "RenderingDom_Diff" + } + } + }], + "type": "Struct" + }, { + "$ast": "MessageDecl", + "attributes": [], + "name": "RendererRenderDom", + "request": { + "$ast": "MessageRequest", + "type": { + "$ast": "ReferenceType", + "name": "RenderingDom" + } + }, + "response": null + }, { + "$ast": "MessageDecl", + "attributes": [], + "name": "RendererRenderDomDiff", + "request": { + "$ast": "MessageRequest", + "type": { + "$ast": "ReferenceType", + "name": "RenderingDom_DiffsInOrder" + } + }, + "response": null }, { "$ast": "UnionDecl", "attributes": [], - "name": "RenderingCommand", + "name": "UnitTest_ElementDescVariant", "members": [{ "$ast": "UnionMember", - "name": "RenderingCommand_BeginBoundary" + "name": "ElementDesc_SolidBorder" }, { "$ast": "UnionMember", - "name": "RenderingCommand_EndBoundary" + "name": "ElementDesc_SinkBorder" }, { "$ast": "UnionMember", - "name": "RenderingCommand_Element" + "name": "ElementDesc_SinkSplitter" + }, { + "$ast": "UnionMember", + "name": "ElementDesc_SolidBackground" + }, { + "$ast": "UnionMember", + "name": "ElementDesc_GradientBackground" + }, { + "$ast": "UnionMember", + "name": "ElementDesc_InnerShadow" + }, { + "$ast": "UnionMember", + "name": "ElementDesc_Polygon" + }, { + "$ast": "UnionMember", + "name": "ElementDesc_SolidLabel" + }, { + "$ast": "UnionMember", + "name": "ElementDesc_ImageFrame" }] }, { "$ast": "StructDecl", "attributes": [], - "name": "RenderingFrame", + "name": "UnitTest_RenderingFrame", "members": [{ "$ast": "StructMember", "name": "frameId", @@ -2566,23 +2631,13 @@ "$ast": "MapType", "element": { "$ast": "ReferenceType", - "name": "ElementDescVariant" + "name": "UnitTest_ElementDescVariant" }, "keyType": { "$ast": "PrimitiveType", "type": "Integer" } } - }, { - "$ast": "StructMember", - "name": "commands", - "type": { - "$ast": "ArrayType", - "element": { - "$ast": "ReferenceType", - "name": "RenderingCommand" - } - } }, { "$ast": "StructMember", "name": "root", @@ -2595,7 +2650,7 @@ }, { "$ast": "StructDecl", "attributes": [], - "name": "RenderingTrace", + "name": "UnitTest_RenderingTrace", "members": [{ "$ast": "StructMember", "name": "createdElements", @@ -2633,7 +2688,7 @@ "$ast": "ArrayType", "element": { "$ast": "ReferenceType", - "name": "RenderingFrame" + "name": "UnitTest_RenderingFrame" } } }], diff --git a/Import/Metadata/RemoteProtocol/Protocol_Renderer.txt b/Import/Metadata/RemoteProtocol/Protocol_Renderer.txt index 299c3430..9fccfd5e 100644 --- a/Import/Metadata/RemoteProtocol/Protocol_Renderer.txt +++ b/Import/Metadata/RemoteProtocol/Protocol_Renderer.txt @@ -37,6 +37,7 @@ struct ElementRendering struct ElementBoundary { + var id : int; var hitTestResult : WindowHitTestResult?; var cursor: WindowSystemCursorType?; var bounds : Rect; diff --git a/Import/Metadata/RemoteProtocol/Protocol_SyncDom.txt b/Import/Metadata/RemoteProtocol/Protocol_SyncDom.txt index b1854ccd..6e551b61 100644 --- a/Import/Metadata/RemoteProtocol/Protocol_SyncDom.txt +++ b/Import/Metadata/RemoteProtocol/Protocol_SyncDom.txt @@ -1,62 +1,39 @@ -union ElementDescVariant -{ - ElementDesc_SolidBorder, - ElementDesc_SinkBorder, - ElementDesc_SinkSplitter, - ElementDesc_SolidBackground, - ElementDesc_GradientBackground, - ElementDesc_InnerShadow, - ElementDesc_Polygon, - ElementDesc_SolidLabel, - ElementDesc_ImageFrame, -} - -class RenderingDom +struct RenderingDomContent { var hitTestResult: WindowHitTestResult?; var cursor: WindowSystemCursorType?; var element: int?; var bounds: Rect; var validArea: Rect; +} + +class RenderingDom +{ + var id: int; + var content: RenderingDomContent; var children: RenderingDom[]; } -struct RenderingCommand_BeginBoundary +enum RenderingDom_DiffType { - var boundary: ElementBoundary; + Deleted, + Created, + Modified, } -struct RenderingCommand_EndBoundary +struct RenderingDom_Diff { + var id: int; + var diffType: RenderingDom_DiffType; + var content: RenderingDomContent?; + var children: int[]; } -struct RenderingCommand_Element +struct RenderingDom_DiffsInOrder { - var rendering: ElementRendering; - var element: int?; + var diffsInOrder: RenderingDom_Diff[]; } -union RenderingCommand -{ - RenderingCommand_BeginBoundary, - RenderingCommand_EndBoundary, - RenderingCommand_Element, -} -struct RenderingFrame -{ - var frameId : int; - var frameName : string?; - var windowSize: WindowSizingConfig; - var elements: ElementDescVariant[int]; - var commands: RenderingCommand[]; - var root: RenderingDom; -} - -struct RenderingTrace -{ - var createdElements: RendererType[int]; - var imageCreations: ImageCreation[.id]; - var imageMetadatas: ImageMetadata[.id]; - var frames: RenderingFrame[]; -} \ No newline at end of file +message RendererRenderDom { request: RenderingDom; } +message RendererRenderDomDiff { request: RenderingDom_DiffsInOrder; } \ No newline at end of file diff --git a/Import/Metadata/RemoteProtocol/Protocol_UnitTest.txt b/Import/Metadata/RemoteProtocol/Protocol_UnitTest.txt new file mode 100644 index 00000000..73dddf84 --- /dev/null +++ b/Import/Metadata/RemoteProtocol/Protocol_UnitTest.txt @@ -0,0 +1,29 @@ +union UnitTest_ElementDescVariant +{ + ElementDesc_SolidBorder, + ElementDesc_SinkBorder, + ElementDesc_SinkSplitter, + ElementDesc_SolidBackground, + ElementDesc_GradientBackground, + ElementDesc_InnerShadow, + ElementDesc_Polygon, + ElementDesc_SolidLabel, + ElementDesc_ImageFrame, +} + +struct UnitTest_RenderingFrame +{ + var frameId : int; + var frameName : string?; + var windowSize: WindowSizingConfig; + var elements: UnitTest_ElementDescVariant[int]; + var root: RenderingDom; +} + +struct UnitTest_RenderingTrace +{ + var createdElements: RendererType[int]; + var imageCreations: ImageCreation[.id]; + var imageMetadatas: ImageMetadata[.id]; + var frames: UnitTest_RenderingFrame[]; +} \ No newline at end of file diff --git a/Import/Metadata/RemoteProtocol/Protocols.txt b/Import/Metadata/RemoteProtocol/Protocols.txt index 6ce9a255..b846776c 100644 --- a/Import/Metadata/RemoteProtocol/Protocols.txt +++ b/Import/Metadata/RemoteProtocol/Protocols.txt @@ -4,4 +4,5 @@ Protocol_IO Protocol_Renderer_BasicElements Protocol_Renderer_ImageFrame Protocol_Renderer -Protocol_SyncDom \ No newline at end of file +Protocol_SyncDom +Protocol_UnitTest \ No newline at end of file diff --git a/Import/VlppOS.cpp b/Import/VlppOS.cpp index 34feae2d..bbd88082 100644 --- a/Import/VlppOS.cpp +++ b/Import/VlppOS.cpp @@ -466,6 +466,7 @@ Licensed under https://github.com/vczh-libraries/License #if defined VCZH_ARM +#include #elif defined VCZH_MSVC || defined VCZH_GCC #include #endif diff --git a/Tools/Reflection32.bin b/Tools/Reflection32.bin index fdf89757623f8a253917d9fe73cacd3607785105..644fb3acba8eea07ced0e8dee3a44e1cf14be60a 100644 GIT binary patch delta 53126 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 delta 52490 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%0DZ&=ZVJC=6^J1n~85D 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 GS^5vE{_Fn$ delta 783 zcmY*W-%Aux6!y-pGk113y&^QTy6CJXEP=LK7%X8MgcWQ{k!b4tjI1_v*K&#Mr5+-P zupYiYpvRzx5H@;>+(4)x3LgT6{sfa=l6;Wu%sMM};hyi_bH4kXA9sC~t*^4>PZB=M zG5nE|NVq-NU^)gu2402w)V`*@)raL!0?&tI$cIuLErHj^g~f&P%v>gOrF^$gyorf$ zQjY~;9~aJas<;4Zko`vt;^}qMy)Mo$y delta 703 zcma)4&ubGw6z=S1XJ?W$-KyA4t<4WB4XA012Wt>*?ZH!9ikQ-7H*qDUq{OV4fYys1 zq)=3P_&hf^FM?Eh5)@R5trrozR1f|KYQ5u4k z+QN-Y=924{7aZ*PPNaigxW%1t)-4r7p1;K%|CQRUQe_dtS`5$Clen)YF{P&QRXvAw zJ%t1}@J$zzd#Mfl)Fb$h}h%YWFZ)Jjgd>YPDSb*z_U`H-?l z@Mmu9KW=@PH{jZ%=*`XGL7v{A&8^vjNj%K0VZ}00w*@lRKwGbn{r;MX6c_&HkT)B{ zhBZrb3wx1q+_ax~g+_6YQm1JBWh(q--lW0?M#wjKUEo#z6O)^d3*!o&IwpMyfgjG& EU*SN`kN^Mx